bzoj 4589 Hard Nim —— FWT

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4589

先手必败,是一开始所有石子的异或和为0;

生成函数 (xpri[1] + xpri[2] + ... + xpri[k])n,pri[k] <= m

FWT求解即可;

而且不要快速幂里面每次变换来变换去的,只有快速幂前后需要变换。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const xn=(1<<16),mod=1e9+7;
int n,m,a[xn],b[xn],lim,inv,cnt,pri[xn];
bool vis[xn];
void init()
{
  int mx=xn-1;
  for(int i=2;i<=mx;i++)
    {
      if(!vis[i])pri[++cnt]=i;
      for(int j=1;j<=cnt&&(ll)i*pri[j]<=mx;j++)
    {
      vis[i*pri[j]]=1;
      if(i%pri[j]==0)break;
    }
    }
}
int upt(int x){while(x>=mod)x-=mod; while(x<0)x+=mod; return x;}
ll pw(ll a,int b)
{
  ll ret=1;
  for(;b;b>>=1,a=(a*a)%mod)if(b&1)ret=(ret*a)%mod;
  return ret;
}
void fwt(int *a,int tp)
{
  for(int mid=1;mid<lim;mid<<=1)
    for(int j=0,len=(mid<<1);j<lim;j+=len)
      for(int k=0;k<mid;k++)
    {
      int x=a[j+k],y=a[j+mid+k];
      a[j+k]=upt(x+y); a[j+mid+k]=upt(x-y);
      if(tp==-1)a[j+k]=(ll)a[j+k]*inv%mod,a[j+mid+k]=(ll)a[j+mid+k]*inv%mod;
    }
}
int main()
{
  inv=pw(2,mod-2); init();
  while(scanf("%d%d",&n,&m)==2)
    {
      memset(a,0,sizeof a);
      for(int i=2;i<=m;i++)if(!vis[i])a[i]=1;
      memset(b,0,sizeof b); b[0]=1;
      lim=1; while(lim<=m)lim<<=1;
      fwt(a,1); fwt(b,1);
      for(;n;n>>=1)
    {
      if(n&1)for(int i=0;i<lim;i++)b[i]=(ll)a[i]*b[i]%mod;
      for(int i=0;i<lim;i++)a[i]=(ll)a[i]*a[i]%mod;
    }
      fwt(b,-1);
      printf("%d\n",b[0]);
    }
  return 0;
}

 

posted @ 2018-11-29 18:07  Zinn  阅读(176)  评论(0编辑  收藏  举报