[BZOJ]4589 Hard Nim

  小C在學FWT是無心間翻到的一道裸題。html

Description

  Claris和NanoApe在玩石子游戲,他們有n堆石子,規則以下:
  1. Claris和NanoApe兩我的輪流拿石子,Claris先拿。
  2. 每次只能從一堆中取若干個,可將一堆全取走,但不可不取,拿到最後1顆石子的人獲勝。
  不一樣的初始局面,決定了最終的獲勝者,有些局面下先拿的Claris會贏,其他的局面Claris會負。
  Claris很好奇,若是這n堆石子知足每堆石子的初始數量是不超過m的質數,並且他們都會按照最優策略玩遊戲,那麼NanoApe能獲勝的局面有多少種。
  因爲答案可能很大,你只須要給出答案對10^9+7取模的值。

Input

  輸入文件包含多組數據,以EOF爲結尾。
  對於每組數據:共一行兩個正整數n和m。數組

Output

  對於每組數據,輸出一個整數做爲答案。學習

Sample Input

  3 7
  4 13spa

Sample Output

  6
  120code

HINT

  每組數據有1<=n<=10^9,2<=m<=50000。不超過80組數據。htm

 

Solution

  首先是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]);
    }
}

 

Last Word

  原本《關於快速沃爾什變換(FWT)的一點學習和思考》要藉着這道題說出來的,因爲篇幅過長,只好另起一篇了。

相關文章
相關標籤/搜索