BZOJ 4589 Hard Nim

Description

Claris和NanoApe在玩石子游戏,他们有n堆石子,规则如下:
1. Claris和NanoApe两个人轮流拿石子,Claris先拿。
2. 每次只能从一堆中取若干个,可将一堆全取走,但不可不取,拿到最后1颗石子的人获胜。
不同的初始局面,决定了最终的获胜者,有些局面下先拿的Claris会赢,其余的局面Claris会负。
Claris很好奇,如果这n堆石子满足每堆石子的初始数量是不超过m的质数,而且他们都会按照最优策略玩游戏,那么NanoApe能获胜的局面有多少种。
由于答案可能很大,你只需要给出答案对10^9+7取模的值。

Input

输入文件包含多组数据,以EOF为结尾。
对于每组数据:
共一行两个正整数n和m。
每组数据有1<=n<=10^9, 2<=m<=50000。
不超过80组数据。

Output

 

Sample Input

3 7
4 13

Sample Output

6
120
对于该游戏SG[i]=i
根据SG定理,如果n堆石子的SG异或和为0则后手必胜
问题变成了,从小于m的素数中可重复选n个,异或和为0,有多少方案
当n=2时
可以理解为2个多项式“相乘”
$C[i]=\sum_{j^k=i}A[j]*B[k]$
这是类似与卷积的形式,其实这可以用FWT$O(mlogm)$实现
但不可能我们做n次FWT,因为n最大$10^{9}$
我们发现实际上一次FWT视作一次乘法,就可以看作初始数组$A^{n}$
A[i]=[i是素数]
于是可以快速幂
但实际上没必要在快速幂时用UFWT(逆运算),最后将答案数组UFWT就行了
这样比每次“相乘”时都FWT和UFWT要好了不少,从$O(mlogmlogn)$变成$O(mlogm+mlogn)$
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 using namespace std;
  7 bool vis[50001];
  8 int tot,prime[50001],Mod=1e9+7,inv2,n,m;
  9 int f[200001],a[200001];
 10 void pre()
 11 {int i,j;
 12   for (i=2;i<=50000;i++)
 13     {
 14       if (vis[i]==0)
 15     {
 16       tot++;
 17       prime[tot]=i;
 18     }
 19       for (j=1;j<=tot;j++)
 20     {
 21       if (i*prime[j]>50000) break;
 22       vis[i*prime[j]]=1;
 23       if (i%prime[j]==0) break;
 24     }
 25     }
 26 }
 27 int qpow(int x,int y)
 28 {
 29   int res=1;
 30   while (y)
 31     {
 32       if (y&1) res=1ll*res*x%Mod;
 33       x=1ll*x*x%Mod;
 34       y>>=1;
 35     }
 36   return res;
 37 }
 38 void FWT(int *A,int len)
 39 {int i,j,k;
 40   for (i=1;i<len;i<<=1)
 41     {
 42       for (j=0;j<len;j+=(i<<1))
 43     {
 44       for (k=0;k<i;k++)
 45         {
 46           int x=A[j+k],y=A[j+k+i];
 47           A[j+k]=x+y;
 48           if (A[j+k]>=Mod) A[j+k]-=Mod;
 49           A[j+k+i]=x-y;
 50           if (A[j+k+i]<0) A[j+k+i]+=Mod;
 51         }
 52     }
 53     }
 54 }
 55 void UFWT(int *A,int len)
 56 {int i,j,k;
 57   for (i=1;i<len;i<<=1)
 58     {
 59       for (j=0;j<len;j+=(i<<1))
 60     {
 61       for (k=0;k<i;k++)
 62         {
 63           int x=A[j+k],y=A[j+k+i];
 64           A[j+k]=1ll*(x+y)*inv2%Mod;
 65           A[j+k+i]=1ll*(x-y+Mod)*inv2%Mod;
 66         }
 67     }
 68     }
 69 }
 70 int main()
 71 {int len,i;
 72   inv2=qpow(2,Mod-2);
 73   pre();
 74   while (scanf("%d%d",&n,&m)!=EOF)
 75     {
 76       memset(f,0,sizeof(f));
 77       memset(a,0,sizeof(a));
 78       len=1;
 79       while (len<=m) len*=2;
 80       f[0]=1;
 81       for (i=1;i<=tot;i++)
 82     {
 83       if (prime[i]>m) break;
 84       a[prime[i]]=1;
 85     }
 86       FWT(f,len);FWT(a,len);
 87       while (n)
 88     {
 89       if (n&1)
 90         {
 91           for (i=0;i<len;i++)
 92         f[i]=1ll*f[i]*a[i]%Mod;
 93         }
 94       for (i=0;i<len;i++)
 95         a[i]=1ll*a[i]*a[i]%Mod;
 96       n>>=1;
 97     }
 98       UFWT(f,len);
 99       printf("%d\n",f[0]);
100     }
101 }

 

posted @ 2018-02-08 20:04  Z-Y-Y-S  阅读(251)  评论(2编辑  收藏  举报