【TC SRM 518 Div 1】【TC 11469】Nim(Nim游戏)(FWT)

传送门

说真的,以后还是要用(cn.vjudge.net),(vjudge.net)直接让我在这道题上Submit Failed了六次,用国内的服务器丢包率真的低好多。。。


题解:

根据Nim游戏的性质我们知道,先手必败当且仅当所有堆的石子数的异或和为 0 0 0

换句话说,我们需要求在 L L L以内的质数中允许重复地,不考虑顺序地选择 M M M个出来,使得异或和为 0 0 0的方案数。

建立集合幂级数OGF,相当于求 M M M次异或卷积,然后算出点值直接算 M M M次方就行了。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

cs int mod=1e9+7,inv2=mod+1>>1;
inline int add(int a,int b){return (a+=b)>=mod?a-mod:a;}
inline int dec(int a,int b){return (a-=b)<0?a+mod:a;}
inline int mul(int a,int b){static ll r;r=(ll)a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
	for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
	return res;
}
inline void Inc(int &a,int b){(a+=b)>=mod&&(a-=mod);}
inline void Dec(int &a,int b){(a-=b)<0&&(a+=mod);}

class Nim{
	private:
		static cs int P=5e4+7;
		
		bool mark[P];
		std::vector<int> p;
		
		void linear_sieves(int lim){
			memset(mark,0,sizeof mark);
			for(int re i=2;i<=lim;++i){
				if(!mark[i])p.push_back(i);
				for(int re j:p){
					if(i*j>lim)break;
					mark[i*j]=true;
					if(i%j==0)break;
				}
			}
		}
		
		static cs int SIZE=1<<16|1;

		
		int a[SIZE];
		
		void FWT(int *A,int len,int typ){
			for(int re i=1;i<len;i<<=1)
			for(int re j=0;j<len;j+=i<<1)
			for(int re k=0;k<i;++k){
				int x=A[j+k],y=A[i+j+k];
				A[j+k]=add(x,y),A[i+j+k]=dec(x,y);
			}
			if(typ==-1)for(int re inv=power(inv2,__builtin_ctz(len)),i=0;i<len;++i)A[i]=mul(A[i],inv);
		}
		
	public:
		Nim(){}
		int count(int K,int L){
			linear_sieves(L);
			int l=1;while(l<=p.back())l<<=1;
			memset(a,0,l<<2);
			for(int re v:p)a[v]=1;
			FWT(a,l,1);
			for(int re i=0;i<l;++i)a[i]=power(a[i],K);
			FWT(a,l,-1);
			return a[0];
		}
};

#ifdef zxyoi

Nim Solver;

signed main(){
//	freopen("nim.in","r",stdin);
	int K,L;
	std::cin>>K>>L;
	std::cout<<Solver.count(K,L); 
	return 0;
}

#endif
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值