「PKUSC2018」神仙的游戏 - 题解
题意:给出一个01?串,其中?可以代替成为0或1,令 $ F(i) $ 表示是否存在长度为 $ i $ 的border,求 $ (F(1) \times 1 \times 1) \bigoplus (F(2) \times 2 \times 2) \bigoplus (F(3) \times 3 \times 3) \dots \bigoplus (F(n) \times n \times n) $ 。
做法:
对于第一个subtask,枚举每长度的border然后暴力匹配是否存在即可。
对于第二个subtask,字符串哈希判断字符串前后缀是否相同即可。
然后考虑对于一个长度为 $ len $ 的border,那么所有距离为 $ n-len $ 的数字都相同(问号可以转成0或1)。对于一个长度为 $ len $ 的border不存在,当且仅当存在距离为 $ n-len $ 的倍数的数字不同。
对于第四个subtask,枚举记录01的距离即可。
对于第五个subtask,设原串为 $ s $ ,考虑快速记录01距离,当存在距离为x的0和1,都有 $ \sum_{j-i=x}{ [ s[j] != s[i] ] } $ , 若 $ A_{i} = [ s[i] = 1 ] $ , $ B_{i} = [ s[n-i-1] = 1 ] $ , 则 $ A \times B = \sum_{n+j-i=x}{s[j] != s[i]} $ 。FFT/NTT 即可。
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ed=(b);i<=i##ed;i++)
#define per(i,a,b) for(int i=(a),i##ed=(b);i>=i##ed;i--)
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=500010;
int n,lmt,l,r[N*4];
char s[N];
ll a[N*4],b[N*4],ans=0;
bool f[N];
ll ksm(ll x,ll y) { ll s=1;for(;y;y/=2,x=x*x%mod) if(y%2) s=s*x%mod;return s; }
void ntt(ll *A,int opt) {
rep(i,0,lmt-1) if(i<r[i]) swap(A[i],A[r[i]]);
for(int mid=1;mid<lmt;mid<<=1) {
ll Wn=ksm(opt==1? 3:(mod+1)/3,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lmt;j+=R) {
ll w=1;
for(int k=0;k<mid;k++,w=w*Wn%mod) {
ll x=A[j+k],y=w*A[j+mid+k]%mod;
A[j+k]=(x+y)%mod,A[j+mid+k]=(x-y+mod)%mod;
}
}
}
if(opt==-1) {
ll inv=ksm(lmt,mod-2); rep(i,0,lmt-1) A[i]=A[i]*inv%mod;
}
}
int main() {
scanf("%s",s+1),n=strlen(s+1);
rep(i,1,n) {
if(s[i]=='1') a[i-1]=1; else if(s[i]=='0') b[i-1]=1;
}
reverse(b,b+n);
// rep(i,0,n-1) printf("%lld ",a[i]); puts("\n");
// rep(i,0,n-1) printf("%lld ",b[i]); puts("\n");
for(lmt=1,l=0;lmt<=n+n;lmt<<=1,++l);
rep(i,0,lmt-1) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
ntt(a,1),ntt(b,1);
rep(i,0,lmt-1) a[i]=a[i]*b[i]%mod;
ntt(a,-1);
// rep(i,0,n+n-2) printf("%lld ",a[i]); puts("\n");
rep(i,0,n-1) f[i]=(a[n-i-1]==0)&&(a[n+i-1]==0);
per(i,n-1,1) if(f[i]) {
for(int j=i+i;j<n;j+=i) if(!f[j]) { f[i]=0;break; }
}
rep(i,1,n) if(f[n-i]) ans^=(ll)i*i;
printf("%lld\n",ans);
return 0;
}