小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)的一點學習和思考》要藉着這道題說出來的,因爲篇幅過長,只好另起一篇了。