NOI模拟20

这个是安徽的省选诶,猝不及防的四个题,时间分配能力的一个极大地挑战!!

第一题就是个快速求lcm,可以发现只有根号种长度的环!

第二题好像还挺简单的,特殊性质明示性挺强的

三四题确实是不会了,甚至最后一题写都没有写,其实是一个错误的决定,因为瞎贪心一下就能拿很多分吧!!

T3 山河重整

考场上用的\(n^2\)的dp,因为前面肯定是连续的,新加一个数只会增加右边界!

那么我们可以得到一个性质,出现一个数x不能被表示的时候,他前面选出来的数加和一定是x-1

所以我们正难则反,统计不合法的情况,发现我们其实要找的是一个数划分成几个互不相同的数的方案数

这样的数的个数最多是根号n个,所以我们可以dp去找,其实是个套路,不要一个数一个数的加,我们每次给所有数加1

额,意思就是,我们可以每次添加一个数或者给所有数加一,当然每次必须强行给全局加一

发现这个过程如果正着就这样去做的话,好像非常的困难,所以我们就每次给全局加1,或者删掉一个数

这样相当于做一个完全背包了,当然到这里这个题并没有做完,但是已经基本成型,剩下的我需要容斥来算最终的答案

但是发现可以对当前点造成贡献的点一定是\(\frac{1}{2}\)之前,于是我们递归一半,然后在做上面的过程就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=5e5+5;
int n,ans,mod,f[N],g[N];
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
inline int mo(int x){return x>=mod?x-mod:x;}
void sol(int x){
    // cerr<<x<<endl;
    if(x<=1)return ;
    sol(x>>1);
    // cerr<<x<<endl;
    fo(i,0,x)g[i]=0;
    fu(i,x,1)if(i*(i+1)/2<=x){
        fu(j,x,i)g[j]=g[j-i];
        for(int j=0;j+(j+2)*i<=x;j++)g[j+(j+2)*i]=mo(g[j+(j+2)*i]+f[j]);
        fo(j,i,x)g[j]=mo(g[j]+g[j-i]);
    }
    fo(i,(x>>1)+1,x)f[i]=mo(f[i]-g[i]+mod);
}
signed main(){
    freopen("rebuild.in","r",stdin);
    freopen("rebuild.out","w",stdout);
    n=read();mod=read();
    fu(i,n,1)if(i*(i+1)/2<=n){
        // cerr<<i<<endl;
        fu(j,n,i)f[j]=f[j-i];
        f[i]=mo(f[i]+1);
        fo(j,i*2,n)f[j]=mo(f[j]+f[j-i]);
    }f[0]=1;
    // cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
    sol(n);
    // cerr<<1.0*clock()/CLOCKS_PER_SEC<<endl;
    ans=ksm(2,n);
    fo(i,0,n-1)ans=(ans-f[i]*ksm(2,n-i-1)%mod+mod)%mod;
    printf("%lld\n",ans);
    return 0;
}

T4 回忆

这个是个贪心题,发现我们要考虑的只有两个因素

第一个是子树之间的合并,第二个是子树中没有结束的点的向上贡献,于是这个题做完了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=2e5+5;
int T,n,m;
vector<int> e[N];
int dep[N],a[N],b[N],tag[N],mn[N],to[N];
void dfs1(int x,int f){
    dep[x]=dep[f]+1;
    for(int y:e[x])if(y!=f)dfs1(y,x);
}
void dfs2(int x,int f){
    for(int y:e[x])if(y!=f){
        dfs2(y,x);b[y]+=tag[dep[x]];tag[dep[x]]=0;
        if(mn[y]==dep[x])mn[y]=0,b[y]++;
        if(mn[x]&&mn[y]){
            if(mn[x]<mn[y])tag[mn[y]]++;
            else tag[mn[x]]++,mn[x]=mn[y];
        }
        else mn[x]|=mn[y];
        if(b[x]<b[y])swap(b[x],b[y]),swap(a[x],a[y]);
        if(b[y]+2*a[y]>=b[x]){
            int w=b[y]+2*a[y]+b[x];
            a[x]+=(w>>1);b[x]=(w&1);
        }
        else b[x]-=b[y]+2*a[y],a[x]+=b[y]+2*a[y];
    }
    if(to[x]){
        if(!mn[x]){
            mn[x]=to[x];
            if(b[x])b[x]--;
            else if(a[x])a[x]--,b[x]++;
        }
        else mn[x]=min(mn[x],to[x]);
    }
}
void sol(){
    n=read();m=read();
    fo(i,1,n-1){
        int x=read(),y=read();
        e[x].push_back(y);e[y].push_back(x);
    }
    dfs1(1,0);
    fo(i,1,m){
        int x=read(),y=read();
        if(!to[y]||to[y]>dep[x])to[y]=dep[x];
    }
    dfs2(1,0);
    printf("%d\n",a[1]+b[1]);
    fo(i,1,n)e[i].clear(),dep[i]=to[i]=mn[i]=a[i]=b[i]=tag[i]=0;
}
signed main(){
    freopen("memory.in","r",stdin);
    freopen("memory.out","w",stdout);
    T=read();
    while(T--)sol();
}
posted @ 2022-06-08 14:52  fengwu2005  阅读(45)  评论(0编辑  收藏  举报