zoukankan      html  css  js  c++  java
  • 【BZOJ4589】Hard Nim FWT

    【BZOJ4589】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

    题解:NIM游戏先手必胜的条件是所有堆得石子数异或和为0,原因不说了。

    我们来学习fwt吧!

    要想深入理解原理还要去看picks的博客,不过吾等蒟蒻只需要理解一点表面含义即可。

    fwt与fft类似,也是将一个数组变成一个特殊形式使得我们可以直接将两个特殊形式按位相乘,然后再从这个特殊形式变回来。而具体怎么变呢?对于异或运算,有位大神构造出了这样一个变换:

    fwt:将fft的蝴蝶变换部分改为(a[k],a[k+h/2])->(a[k]+a[k+h/2],a[k]-a[k+h/2])。

    ufwt:(a[k],a[k+h/2])->((a[k]+a[k+h/2])/2,(a[k]-a[k+h/2])/2)

    然后就没了,对于本题,直接快速幂+fwt即可。

     

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    typedef long long ll;
    const ll P=1000000007;
    const ll rev=500000004;
    int n,m,len,num;
    const int maxn=50010;
    ll A[(1<<18)+5],B[(1<<18)+5];
    int pri[maxn],np[maxn];
    void fwt(ll *a)
    {
    	int j,k,h;
    	ll x,y;
    	for(h=2;h<=len;h<<=1)
    		for(j=0;j<len;j+=h)
    			for(k=j;k<j+h/2;k++)
    				x=a[k],y=a[k+h/2],a[k]=(x+y)%P,a[k+h/2]=(x-y+P)%P;
    }
    void ufwt(ll *a)
    {
    	int j,k,h;
    	ll x,y;
    	for(h=2;h<=len;h<<=1)
    		for(j=0;j<len;j+=h)
    			for(k=j;k<j+h/2;k++)
    				x=a[k],y=a[k+h/2],a[k]=(x+y)*rev%P,a[k+h/2]=((x-y)*rev%P+P)%P;
    }
    void pow(ll *a,ll y)
    {
    	ll *b=B;
    	b[0]=1;
    	fwt(a),fwt(b);
    	while(y)
    	{
    		if(y&1)
    		{
    			for(int i=0;i<len;i++)	b[i]=a[i]*b[i]%P;
    		}
    		for(int i=0;i<len;i++)	a[i]=a[i]*a[i]%P;
    		y>>=1;
    	}
    	ufwt(b);
    }
    int main()
    {
    	int i,j;
    	for(i=2;i<=50000;i++)
    	{
    		if(!np[i])	pri[++num]=i;
    		for(j=1;j<=num&&i*pri[j]<=50000;j++)
    		{
    			np[i*pri[j]]=1;
    			if(i%pri[j]==0)	break;
    		}
    	}
    	while(scanf("%d%d",&n,&m)!=EOF)
    	{
    		for(len=1;len<=m;len<<=1);
    		memset(A,0,sizeof(A));
    		memset(B,0,sizeof(B));
    		for(i=1;i<=num&&pri[i]<=m;i++)	A[pri[i]]=1;
    		pow(A,n);
    		printf("%lld
    ",B[0]);
    	}
    	return 0;
    }

     

  • 相关阅读:
    Paragon NTFS for Mac免费获取官方赠送正版.更新获取ntfs for mac 14方法
    Python修饰器的函数式编程
    Python装饰器与面向切面编程
    linux配置IP的方法
    Centos搭建SVN服务器三步曲
    [CentOS 6.5 X64]讓firefox java plugin 啟動
    逻辑分析题(三)
    逻辑分析题汇总(一)
    第十章—DOM(0)—NODE类型
    第十章—DOM(三)——Text类型
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7397194.html
Copyright © 2011-2022 走看看