# BZOJ4589 Hard Nim（博弈+FWT）

即使n个数的异或为0。如果只有两堆，将质数筛出来设为1，做一个异或卷积即可。显然这个东西满足结合律，多堆时直接快速幂。可以在点值表示下进行。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define N (1<<17)
#define P 1000000007
#define inv2 500000004
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int T,n,m,f[N];
bool flag[N];
int ksm(int a,int k)
{
int s=1;
for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
return s;
}
void FWT(int *a,int n,int op)
{
for (int i=2;i<=n;i<<=1)
for (int j=0;j<n;j+=i)
for (int k=j;k<j+(i>>1);k++)
{
int x=a[k],y=a[k+(i>>1)];
a[k]=(x+y)%P,a[k+(i>>1)]=(x-y+P)%P;
if (op==1) a[k]=1ll*a[k]*inv2%P,a[k+(i>>1)]=1ll*a[k+(i>>1)]*inv2%P;
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj4589.in","r",stdin);
freopen("bzoj4589.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
flag[1]=flag[0]=1;
for (int i=2;i<=50000;i++)
for (int j=2;j<=50000/i;j++)
flag[i*j]=1;
while (scanf("%d %d",&n,&m)!=EOF)
{
int t=1;while (t<=m) t<<=1;
for (int i=0;i<=m;i++) f[i]=flag[i]^1;
for (int i=m+1;i<t;i++) f[i]=0;
FWT(f,t,0);
for (int i=0;i<t;i++) f[i]=ksm(f[i],n);
FWT(f,t,1);
cout<<f[0]<<endl;
}
}

posted @ 2019-01-01 16:34  Gloid  阅读(121)  评论(0编辑  收藏