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 read()
{
    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;
    }
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/10205111.html