NOIP提高组模拟赛7

A. 工业题 / a

带取模的运算,除以一个数一定要乘逆元!!!!!!!!!!!!!!!!!!

不同位置的值对最终答案的贡献是互不影响的,分开考虑每个值的贡献

考虑对于位于\((x,y)\)的值对\((n,m)\)的贡献,无论从哪个路径走过去,一定是原数\(*a^{m-x}*b^{n-y}\),而对答案贡献多少次即为\((x,y)->(n,m)\)不同的路径数,这个显然是个组合数,用l表示需要走几步,r表示需要向右(或下)走几步,那么方案数为\(C_l^r\),为了方便计算,我们从方案数唯一的开始算,这样每次通过\(C_l^r\),求\(C_{l+1}^{r+1}\)只需要\(*(l+1)/(r+1)\)记得用逆元

#include<cstdio>
#include<cstring>
using namespace std;
const int mod=998244353;
long long a,b;
long long x[300005],y[300005];
int n,m;
long long qp(long long x,long long y){
    long long ans=1;
    while(y){
        if(y&1)ans=(ans*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return ans;
}
int main(){
    scanf("%d%d%lld%lld",&n,&m,&a,&b);
    for(int i=1;i<=n;++i)scanf("%lld",&x[i]);
    for(int i=1;i<=m;++i)scanf("%lld",&y[i]);
    for(int i=1;i<=n;++i)x[i]%=mod;
    for(int i=1;i<=m;++i)y[i]%=mod;
    a%=mod;b%=mod;
    long long nm=qp(a,m),nn=qp(b,n);
    long long c=1,l=m-1,r=0,ans=0,num=nm;
    for(int i=n;i>=1;--i){
        ans=(ans+((x[i]*c)%mod*num)%mod)%mod;
        ++l,++r;c=(c*(l%mod)%mod*qp(r,mod-2))%mod;num=(num*b)%mod;
    }
    c=1,l=n-1,r=0,num=nn;
    for(int i=m;i>=1;--i){
        ans=(ans+((y[i]*c)%mod*num)%mod)%mod;
        ++l,++r;c=((c*l)%mod*qp(r,mod-2))%mod;num=(num*a)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

B. 卡常题 / b

n个点,2n条边,还是连通图,那么图中有且只有一个环,是一棵基环树

如果将一个y点和其黑白边看成一条边,可以省去y点,将黑白边当作点权加到x点上,这样仍然得到基环树

其实问题已经可以转化为在基环树上任意相邻两点必须有一个激活,激活费用为点权的最小花费

任意一条边所连的两点必然有一个被激活,我们考虑断掉环上的一边,转化为普通的树形DP,两次DP分别激活断开的两点,取个min即可

#include<cstdio>
#include<cstring>
using namespace std;
int min(int x,int y){return x<y?x:y;}
const int maxn=1000005;
struct edge{
    int to,net;
}e[maxn<<1|1];
int d[maxn],head[maxn],tot=1;
void add(int u,int v){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
}
bool vis[maxn];
int del,root1,root2;
bool dfs(int x,int fx){
    vis[x]=1;
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fx)continue;
        if(vis[v]){del=i;root1=x;root2=v;return 1;}
        if(dfs(v,x))return 1;
    }
    return 0;
}
int f[maxn][2];
void work(int x,int fx){
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(v==fx||i==del||i==(del^1))continue;
        work(v,x);
        f[x][0]+=f[v][1];
        f[x][1]+=min(f[v][0],f[v][1]);
    }
    f[x][1]+=d[x];
}
int n;
int main(){
    int a,b;
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;++i){
        int u,v;scanf("%d%d",&u,&v);
        d[u]+=a;d[v]+=b;add(u,v);add(v,u);
    }
    bool p=dfs(1,0);
    work(root1,0);
    int ans=f[root1][1];
    memset(f,0,sizeof(f));
    work(root2,0);
    ans=min(ans,f[root2][1]);
    printf("%d\n",ans);
    return 0;
}

C. 玄学题 / c

考场脑抽系列

发现是完全平方数才会有影响(奇数个因子),

枚举n只需求对已知\(i(i\in[1,n])[1,m]\)中有多少数与之相乘为完全平方数

将i写成\(q*p_1^2\)的形式,其中q不含平方因子,与i相乘为完全平方数的数一定能写成\(q*p_2^2\)

问题转化为求小于等于\(m/q\)的完全平方数有几个,开方即可,不难想到

\(sqrt(m/q)\)即为所求

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=10000005;
long long a[maxn];
long long n,m;
void work(){
    for(long long i=1;i*i<=n;++i)
     for(long long j=1;j*i*i<=n;++j)
       a[i*i*j]=j;
    long long ans=0;
    for(long long i=1;i<=n;++i)
     if(((long long)sqrt(m/a[i]))&1)--ans;
     else ++ans;
    printf("%lld\n",ans);
}
int main(){
    scanf("%lld%lld",&n,&m);
    work();
    return 0;
}
上一篇:Pr 2021快速入门教程,如何在PR中使用音频编辑?


下一篇:单调栈解决leetcode-84柱状图中最大的矩形