bzoj4589 Hard Nim

题目描述

有 $n$ 堆石子,每堆石子是不超过 $m$ 的质数,求有多少种局面,使 $Nim$ 游戏中先手获胜

数据范围

$n \le 10^9,m \le 50000$

题解

首先我们知道 $Nim$ 游戏中先手获胜的条件是 $n$ 堆石子异或值为 $0$

于是我们就 $Fwt$ +快速幂求出第 $0$ 项的值即可

效率: $O(mlog(nm))$

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1<<17,P=1e9+7,i2=(P+1)>>1;
int n,m,t,p[N],c,a[N],b[N];bool F[N];
int X(int x){return x>=P?x-P:x;}
void Fwt(int *g,int o){
    for (int i=1;i<t;i<<=1)
        for (int j=0;j<t;j+=(i<<1))
            for (int x,y,k=0;k<i;k++){
                x=g[j+k];y=g[i+j+k];
                g[j+k]=X(x+y);g[i+j+k]=X(x-y+P);
                if (!o) g[j+k]=1ll*g[j+k]*i2%P,
                    g[i+j+k]=1ll*g[i+j+k]*i2%P;
            }
}
int main(){
    for (int i=2;i<N;i++){
        if (!F[i]) p[++c]=i;
        for (int v,j=1;j<=c;j++){
            v=p[j]*i; if (v>=N) break;
            F[v]=1; if (i%p[j]==0) break;
        }
    }
    while(~scanf("%d%d",&n,&m)){
        for (int j=1;j<=c;j++)
            if (p[j]<=m) a[p[j]]=1;
        for (t=1;t<=m;t<<=1);b[0]=1;
        Fwt(a,1);Fwt(b,1);
        while(n){
            if (n&1)
                for (int i=0;i<t;i++)
                    b[i]=1ll*b[i]*a[i]%P;
            for (int i=0;i<t;i++)
                a[i]=1ll*a[i]*a[i]%P;n>>=1;
        }
        Fwt(b,0);printf("%d\n",b[0]);
        for (int i=0;i<t;i++) a[i]=b[i]=0;
    }
    return 0;
}

 

posted @ 2020-01-28 20:33  xjqxjq  阅读(103)  评论(0编辑  收藏  举报