bzoj 4589 Hard Nim
这里用 (oplus) 表示 (xor) 运算 或 异或卷积.
众所周知,(Nim) 博弈的结论:当且仅当 (a_1oplus a_2 oplus dots oplus a_n=0) 时,后手必胜,否则先手必胜.- 考虑构造一个数列 (a,a_i=1) 当且仅当 (i) 为小于等于 (m) 的质数,否则 (a_i=0) .
- 如果 (n=2) ,令 (b=aoplus a,b_0) 即为答案.可以推广, (n) 堆石头,就令 (n) 个 (a) 连续做异或卷积,最后的 (b_0) 即为答案.
- 这两步的结论(大概)可以通过结合 (Nim) 博弈的结论和异或卷积的定义,感性理解得出.
- 注意到:
[FWT(Aoplus B oplus C)=FWT(IFWT(FWT(A) imes FWT(B))) imes FWT(C)\
=FWT(A) imes FWT(B) imes FWT(C)\
=FWT(A) imes( FWT(B) imes FWT(C))
]
- 所以可以算出 (FWT(a)) 后,令每个 (a_i=a_i^n) ,再对 (a) 做 (IFWT) ,得到的 (a_0) 即为答案.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pii pair<int,int>
inline int read()
{
int x=0;
bool pos=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
pos=0;
for(;isdigit(ch);ch=getchar())
x=x*10+ch-'0';
return pos?x:-x;
}
const int P=1e9+7;
const int inv2=500000004;
inline int add(int a,int b)
{
return (a + b) % P;
}
inline int mul(int a,int b)
{
return 1LL * a * b % P;
}
int fpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
const int MAXM=5e4+10;
int prime[MAXM],cnt=0,ism[MAXM];
void init_prime()
{
for(int i=2;i<=50000;++i)
{
if(!ism[i])
prime[++cnt]=i;
for(int j=1;j<=cnt && i*prime[j]<=50000;++j)
{
ism[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
void FWT_xor(int *a,int n)
{
for(int l=2;l<=n;l<<=1)
{
int m=l>>1;
for(int *p=a;p!=a+n;p+=l)
{
for(int i=0;i<m;++i)
{
int x0=p[i],x1=p[i+m];
p[i]=add(x0,x1);
p[i+m]=add(x0,P-x1);
}
}
}
}
void IFWT_xor(int *a,int n)
{
for(int l=2;l<=n;l<<=1)
{
int m=l>>1;
for(int *p=a;p!=a+n;p+=l)
{
for(int i=0;i<m;++i)
{
int x0=p[i],x1=p[i+m];
p[i]=mul(add(x0,x1),inv2);
p[i+m]=mul(add(x0,P-x1),inv2);
}
}
}
}
int n,m;
int a[MAXM<<1];
int main()
{
init_prime();
while(~scanf("%d %d",&n,&m))
{
int N=1;
while(N<=m)
N<<=1;
memset(a,0,sizeof a);
for(int i=2;i<=m;++i)
a[i]=(!ism[i]);
FWT_xor(a,N);
for(int i=0;i<N;++i)
a[i]=fpow(a[i],n);
IFWT_xor(a,N);
cout<<a[0]<<endl;
}
return 0;
}