[PKUSC2018]神仙的遊戲 題解
阿新 • • 發佈:2020-10-18
[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\)
我們可以得到一個這樣的性質:若任取 \(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; }