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();
}
QQ:2953174821