[PKUSC2018]神仙的游戏 题解
[PKUSC2018]神仙的游戏 题解
Problem
小D和小H发现了一种新的游戏。
给出一个由0/1/?组成的字符串 \(s\) ,将 \(s\) 中的问号用0/1替换,对每个 \(l\) 口算是否存在替换问号的方案使得 \(s\) 长度为 \(l\) 的前缀成为border,把这个结果记做 \(f(l)=0/1\) 。
请计算 \((f(1) \times 1^2) \otimes (f(2) \times 2^2) \otimes \cdots \otimes (f(n) \times n^2)\) 。
\(|s| \leq 5 \times 10^5\)
Solution
对于一个长度为 \(len\) 的border,必定会有 \(s_{i}=s_{n-len+i}\) 。
我们可以得到一个这样的性质:若任取 \(i \equiv j (\mod x)\) ,都有 \(s_i=s_j\) ,则 \(n-x\) 是一个border。
所以,倘若 \(i \equiv j (\mod x)\) ,\(s_i \neq s_j\) ,则 \(n-x\) 必定不是一个border。即只要知道有一对01的差是 \(d\) ,就一定会有对于一个 \((n-len)|d\) , \(len\) 都不是一个border。
所以我们只要求出两两01的差,就能判断border,而这个只要FFT即可。
Code
#include<bits/stdc++.h>
#define DB double
#define LL long long
using namespace std;
const DB Pi=acos(-1);
LL n,Ans;
char s[500005];
int vis[500005],rev[2000005];
struct Complex{
DB x,y;
inline Complex operator + (const Complex &A)const{
return (Complex){x+A.x,y+A.y};
}
inline Complex operator - (const Complex &A)const{
return (Complex){x-A.x,y-A.y};
}
inline Complex operator * (const Complex &A)const{
return (Complex){x*A.x-y*A.y,x*A.y+y*A.x};
}
}F[2000005],G[2000005];
void FFT(Complex *F,int Lim,int op){
for(register int i=0;i<Lim;++i){
if(i<rev[i])
swap(F[i],F[rev[i]]);
}
for(register int mid=1;mid<Lim;mid<<=1){
int R=mid<<1;
Complex rt=(Complex){cos(Pi/mid),op*sin(Pi/mid)};
for(register int j=0;j<Lim;j+=R){
Complex w=(Complex){1,0};
for(register int k=0;k<mid;++k){
Complex x=F[j|k],y=w*F[j|k|mid];
F[j|k|mid]=x-y;
F[j|k]=x+y;
w=w*rt;
}
}
}
if(op==-1){
for(register int i=0;i<Lim;++i){
F[i].x/=Lim;
F[i].y/=Lim;
}
}
return;
}
int main(){
scanf("%s",s+1);n=strlen(s+1);
int Lim=1,Len=-1;
while(Lim<(n+1<<1))
Lim<<=1,++Len;
for(register int i=0;i<Lim;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)<<Len);
for(register int i=0;i<n;++i)
F[i].x=(s[i+1]=='0');
for(register int i=0;i<n;++i)
G[i].x=(s[n-i]=='1');
FFT(F,Lim,+1);FFT(G,Lim,+1);
for(register int i=0;i<Lim;++i)
F[i]=F[i]*G[i];
FFT(F,Lim,-1);
for(register int i=1;i<(n<<1);++i)
vis[abs(i-n+1)]+=(F[i].x+0.5);
for(register int i=1;i<n;++i){
int flag=1;
for(register int j=1;i*j<=n;++j){
if(vis[i*j]){
flag=0;
break;
}
}
if(flag)
Ans^=1LL*(n-i)*(n-i);
}
printf("%lld\n",Ans^(1LL*n*n));
return 0;
}