小C在学FWT是无心间翻到的一道裸题。html
输入文件包含多组数据,以EOF为结尾。
对于每组数据:共一行两个正整数n和m。数组
对于每组数据,输出一个整数做为答案。学习
3 7
4 13spa
6
120code
每组数据有1<=n<=10^9,2<=m<=50000。不超过80组数据。htm
首先是Nim游戏,根据Nim游戏的结论,若全部石子的数量异或起来等于0,那么后手必胜,不然先手必胜。blog
因此咱们要求的是n个数有多少种取值方案会使得这n个数的异或和为0。游戏
咱们先考虑n=2的时候怎么作。ip
虽然咱们知道,答案等于m之内的质数个数,可是咱们仍是要想想最朴素的作法。get
枚举前一个数,枚举后一个数,看看他们的异或和是否等于0。
这样是O(m^2)的,并且咱们可以获得的信息不仅是异或和为0的方案数,甚至咱们连异或和为x(x>0)的方案数都知道了。
而后仔细想一想这不就是在作FWT吗?
(快速沃尔什变换FWT:http://www.cnblogs.com/ACMLCZH/p/8022502.html)
而后n>2呢?那大概就是n个这样的数组卷积在一块儿吧。
因为卷积具备结合律,因此搞个卷积快速幂就能够了。
时间复杂度O(T*logn*mlogm)。
#include <cstdio> #include <cstring> #include <algorithm> #define MM 135005 #define mod 1000000007 using namespace std; int ntw,n,m,prin,len; int pri[MM],a[MM],b[MM]; bool u[MM]; inline int read() { int n=0,f=1; char c=getchar(); while (c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();} while (c>='0' && c<='9') {n=n*10+c-'0'; c=getchar();} return n*f; } void FWT(int *a,int len,int g) { register int wt,st,i,x,y; for (wt=1;wt<len;wt<<=1) for (st=0;st<len;st+=wt<<1) for (i=0;i<wt;++i) { x=a[st+i]; y=a[st+wt+i]; a[st+i]=1LL*g*(x+y)%mod; a[st+wt+i]=1LL*g*(x-y+mod)%mod; } } inline void pro(int* a,int* b,int len) {for (register int i=0;i<len;++i) a[i]=1LL*a[i]*b[i]%mod;} void mi(int* b,int* a,int z,int len) { FWT(b,len,1); FWT(a,len,1); for (;z;z>>=1,pro(a,a,len)) if (z&1) pro(b,a,len); FWT(b,len,ntw); } int main() { register int i,j; for (i=2;i<MM;++i) { if (!u[i]) pri[++prin]=i; for (j=1;i*pri[j]<MM;++j) { u[i*pri[j]]=true; if (i%pri[j]==0) break; } } ntw=(mod+1)/2; while (scanf("%d%d",&n,&m)!=EOF) { memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for (i=1;pri[i]<=m;++i) ++a[pri[i]]; for (len=1;len<=pri[i-1];len<<=1); b[0]=1; mi(b,a,n,len); printf("%d\n",b[0]); } }
原本《关于快速沃尔什变换(FWT)的一点学习和思考》要借着这道题说出来的,因为篇幅过长,只好另起一篇了。