【考试总结】2022-06-03

石子游戏

将问题转化为求全集的最小的子集使得其异或和为全部元素异或和

H 为所有元素构成的集合幂级数,Fi 表示用 i 个互不相同的元素异或得到的结果的集合幂级数,不难找到转移式 Fi=Fi1HFi2(ni+2)(i1)

暴力的想法就是计算出来 Fi 的点值后每次做逆变换判定是不是目标位置非零

注意到要张成 所有元素张成的空间 最少需要线性基秩大小个元素,也就是 Θ(log) 个,那么每次根据定义式求逆变换后目标位置系数复杂度就是对的

我写了四模数来防止非零

Code Display
const int N=530000;
int n;
struct data{
	int a,b,c;
	ull d;
	data(){}
	data(int aa,int bb,int cc,ull dd){a=aa; b=bb; c=cc; d=dd;}
	data operator +(const data &x)const{
		return data(add(a,x.a,mod1),add(b,x.b,mod2),add(c,x.c,mod3),d+x.d);
	}
	data operator -(const data &x)const{
		return data(del(a,x.a,mod1),del(b,x.b,mod2),del(c,x.c,mod3),d-x.d);
	}
	data operator *(const int &x)const{
		return data(mul(a,x,mod1),mul(b,x,mod2),mul(c,x,mod3),d*x);
	}
	data operator *(const data &x)const{
		return data(mul(a,x.a,mod1),mul(b,x.b,mod2),mul(c,x.c,mod3),d*x.d);
	}
}f[3][N],H[N];
inline void FWT(data *f,int lim){
	for(int p=2;p<=lim;p<<=1){
		int len=p>>1;
		for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l){
			data x=f[l],y=f[l+len];
			f[l]=x+y;
			f[l+len]=x-y;
		}
	}
}
signed main(){
	n=read();
	int tar=0,Mx=0,U=1;
	rep(i,1,n){
		int x=read();
		ckmax(Mx,x);
		f[1][x].a++;
		f[1][x].b++;
		f[1][x].c++;
		f[1][x].d++;
		tar^=x;
	}
	if(!tar) print(n),exit(0);
	if(f[1][tar].a) print(n-1),exit(0);
	while(U<=Mx) U<<=1; --U;
	f[0][0]=data(1,1,1,1);
	FWT(f[0],U+1);
	FWT(f[1],U+1);
	rep(i,0,U) H[i]=f[1][i];
	int num=2;
	while(1){
		int p1=num%3,p2=(num-1)%3,p3=(num-2)%3;
		for(int i=0;i<=U;++i){
			int v=(num-1)*(n-num+2);
			data tmp={v%mod1,v%mod2,v%mod3,v};
			f[p1][i]=f[p2][i]*H[i]-f[p3][i]*tmp;
		}
		data res=data(0,0,0,0);
		for(int i=0;i<=U;++i){
			int pc=__builtin_popcount(i&tar)&1;
			if(pc) res=res-f[p1][i];
			else res=res+f[p1][i];
		}
		if(res.a||res.b||res.c||res.d) print(n-num),exit(0);
		++num;
	}
	return 0;
}

函数

构造 HG=F ,其中 表示狄利克雷卷积

G(x)=xk 是积性函数,那么 H 也是积性函数

那么手玩发现 H(1)=G(1)=1 ,对于质数 pF(p)=H(p)G(1)+G(p)H(1)G(p)=0

根据 H 是积性函数那么如果某个 n 的唯一分解中有质数指数为 1 那么 H(n)=0 ,所以非零的 n 只有 N

尝试考察 e>1H(pe) 的数值,使用数学归纳法配合等比数列求和可以得到 H(pe)=pkp2k

那么 i=1nF(i)=ijnG(i)H(j)=i=1nH(n)j=1niG(j)

对于所有非零的 H(i) 后半部分自然数幂前缀和算即可

听说这就是 powerful number 筛

Code Display
const int N=8e6+10;
namespace calculator{
	int K;
	int x[50],pre[50],suf[50],y[50],ifac[50];
	inline int Lagrange(int X){
		if(X<=K) return y[X];
		rep(i,1,K) pre[i]=mul(pre[i-1],del(X%mod,i));
		Down(i,K,1) suf[i]=mul(suf[i+1],del(X%mod,i));
		int res=0;
		rep(i,1,K){
			int val=mul(y[i],mul(pre[i-1],suf[i+1]));
			ckmul(val,mul(ifac[i-1],ifac[K-i]));
			if((K-i)&1) val=del(0,val);
			ckadd(res,val);
		}
		return res;
	}	
	inline void init(int k){
		K=k+3;
		pre[0]=suf[K+1]=1;
		rep(i,1,K) x[i]=i,y[i]=add(y[i-1],ksm(x[i],k));
		int fac=1; ifac[0]=1;
		for(int i=1;i<=K;++i){
			ckmul(fac,i);
			ifac[i]=ksm(fac,mod-2);
		}
		return ;
	}
}
int n,k,block;
int pri[N],cnt,id1[N],id2[N];
bool fl[N];
inline int get_id(int x){return x>block?id2[n/x]:id1[x];}
int pw[N],coef[N],val[N],tot;
inline void dfs(int n,int id,int H){
	ckadd(coef[get_id(n)],H);
	for(int i=id+1;i<=cnt&&pri[i]*pri[i]<=n;++i){	
		int y=pri[i];
		int curf=mul(pw[i],del(1,pw[i]));
		do{
			y*=pri[i];
			dfs(n/y,i,mul(H,curf));
		}while(y<=n/pri[i]);
	}	
	return ;
}
signed main(){
    n=read(); k=read(); block=sqrt(n)+10;
	calculator::init(k);
	for(int i=2;i<=block;++i){
        if(!fl[i]){
            pri[++cnt]=i;
			pw[cnt]=ksm(i,k);
        }
        for(int j=1;j<=cnt&&pri[j]*i<=block;++j){
            fl[i*pri[j]]=1;
            if(i%pri[j]==0) break;
		}
    }
    for(int l=1,r;l<=n;l=r+1){
        r=n/(val[++tot]=n/l);
        if(val[tot]<=block) id1[val[tot]]=tot;
        else id2[r]=tot;
    }
	dfs(n,0,1);
	int ans=0;
    for(int i=1;i<=tot;++i) if(coef[i]){
		ckadd(ans,mul(coef[i],calculator::Lagrange(val[i])));
	}
	print(ans);
	return 0;
}

先考察 m=0 的部分,如果某个元素在某位小于限制那么可以让其它元素后续任选,使用它调配异或值

对于有多个元素在当前位小于限制的情况可以设计一个 DP:fi,0/1,0/1 表示这位当前选择的 1 的奇偶性,以及这位是不是已经有元素小于限制

最开始的小于限制的元素数值是一定的,不能附乘 2bit

如果所有元素都贴上界那么可以到删掉这位继续考虑最高位,注意如果 limi=C 要额外加一

考虑使用集合容斥来处理在最终方案中某一权值全部元素的分组情况,也就是钦定一些元素权值相同,不同的钦定之间不要求不同,要求这样的钦定满足边的限制

cS 表示将 S 中元素钦定成相同权值的容斥系数,仍然使用 ABC236Ex 的计算方式,全部连边方式系数之和取决于点集中是否有边,否则枚举 1 所在联通块,判定外部是否没有任何边即可

fS,T 表示已经钦定了 S 中的点,奇数大小集合的最小 lim 取值点为 T 的方案数,最终答案的统计是 f(U,S) 乘对 Slim 做上面 DP 得到的方案数

此处直接枚举添加 S 补集的子集并根据实际含义转移即可,也就是说再添加偶数大小钦定时需要附上 minlim+1 的系数

如果强制添加的 补集的子集 包含补集的最小值那么可以证明复杂度是 Θ(3n) ,另一种强制包含补集 lowbit 的做法也过了,不知道能不能卡

Code Display
int lim[20],n,m,C;
bool legal[1<<15];
int sum[1<<15],coef[1<<15],argmin[1<<15],pw[20];
int f[60000010];
int a[20],dp[20][2][2];
inline int DP(int S){
	int ans=0;
	m=0;
	rep(i,1,n) if(S>>(i-1)&1) a[++m]=lim[i];
	for(int i=60;i>=1;--i){	
		int cnt=0;	
		memset(dp,0,sizeof(dp));
		dp[0][0][0]=1;
		for(int j=1;j<=m;++j){
			int rem=a[j]&((1ll<<(i-1))-1);
			rem=(rem+1)%mod;
			for(int x=0;x<2;++x) for(int y=0;y<2;++y) if(dp[j-1][x][y]){
				if(a[j]>>(i-1)&1){
					if(x) ckadd(dp[j][1][y],mul(dp[j-1][x][y],(1ll<<(i-1))%mod));
					else ckadd(dp[j][1][y],dp[j-1][x][y]);
					ckadd(dp[j][x][y^1],mul(dp[j-1][x][y],rem));
				}else{
					ckadd(dp[j][x][y],mul(dp[j-1][x][y],rem));
				}
			}
			if(a[j]>>(i-1)&1) ++cnt;
		}
		ckadd(ans,dp[m][1][C>>(i-1)&1]);
		if((cnt&1)!=(C>>(i-1)&1)) break;
		if(i==1) ckadd(ans,1);
	}
	return ans;
}
signed main(){
	n=read(); m=read(); C=read();
	rep(i,1,n) lim[i]=read();
	int U=(1<<n)-1;
	for(int i=0;i<=U;++i) legal[i]=1;
	while(m--){
		int u=read(),v=read();
		for(int i=0;i<=U;++i) if((i>>(u-1)&1)&&(i>>(v-1)&1)) legal[i]=0;
	}
	pw[0]=1;
	rep(i,1,n) pw[i]=pw[i-1]*3;
	for(int i=0;i<=U;++i){
		argmin[i]=-1;
		for(int j=1;j<=n;++j) if(i>>(j-1)&1){
			sum[i]+=pw[j];
			if(argmin[i]==-1||lim[j]<lim[argmin[i]]) argmin[i]=j;
		}
	}
	for(int i=1;i<=U;++i){
		int lb=i&(-i);
		coef[i]=legal[i];
		for(int j=(i-1)&i;j;j=(j-1)&i){
			if((j&lb)&&legal[i^j]) ckdel(coef[i],coef[j]);
		}
	}
	f[0]=1;
	for(int S=0;S<=U;++S){
		for(int T=S;;T=(T-1)&S){
			if(f[sum[S]+sum[T]]){
				int oth=U^S;
				for(int x=oth;x;x=(x-1)&oth){
					if((x&(-x))!=(oth&(-oth))) continue;
					if(__builtin_parity(x)){
						ckadd(f[sum[S|x]+sum[T|(1<<(argmin[x]-1))]],mul(f[sum[S]+sum[T]],coef[x]));
					}else{
						ckadd(f[sum[S|x]+sum[T]],mul(f[sum[S]+sum[T]],mul(coef[x],(lim[argmin[x]]+1)%mod)));
					}
				}
			}
			if(!T) break;
		}
	}
	int ans=0;
	for(int i=0;i<=U;++i) if(f[sum[U]+sum[i]]) ckadd(ans,mul(f[sum[U]+sum[i]],DP(i)));
	print(ans);
	return 0;
}

posted @   没学完四大礼包不改名  阅读(121)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示