PKUSC2018除了主斗地都补完啦
好大啊【糟糕的语言
主斗地等什么时候空下来较长的时间再补吧。。。
回到这个题
当时听罗大讲并没有听明白...但是大概还是懂了的 就是我们可以通过找到所有01对位置差来筛掉所有不符合的border 具体证明放下面了
首先我们介绍一点基本的border相关理论
1.长度为x的border代表着 长度为x的前缀与长度为x的后缀相同
2.长度为x的周期代表着 每隔x个字符一截 每一段都相同【除最后一段可能不够长
3.长度为i的border对应着长度为n-i的周期
那么对于一对01位置差为x 那么显然border不能让他们重合 所以对于y|x 一定不存在长度为n-y的border
这个简单画画就可以理解
有了所有的位置差 我们可以筛一下就行了 这就是调和级数啦 所以我们问题解决了一半
接下来我们就要求所有的01位置差
枚举肯定是不行的 但是可以过O(n^2)的67部分分[huaji]
然后我们需要找更好的办法 这时你可以想到原来的FFT匹配字符串 详情可见这里
可以设,
然后正常求卷积的话我们求出来是位置和 根据常见套路我们翻转一个多项式就ok啦
具体写法什么的看看代码比较吼w 这个题也不长 挺好的
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define N 500010 #define mdn 998244353 #define G 3 using namespace std; int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=(ll)ans*bs%mdn; bs=(ll)bs*bs%mdn; mi>>=1; } return ans; } int r[N<<2],inv; int pre(int n) { int l=0,lim=1; while(lim<n) lim<<=1,l++; for(int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1)); inv=ksm(lim,mdn-2); return lim; } void ntt(int *a,int lim,int f) { for(int i=0;i<lim;i++) if(r[i]>i) swap(a[r[i]],a[i]); for(int k=2,mid=1;k<=lim;k<<=1,mid<<=1) { int Wn=ksm(G,(mdn-1)/k); if(f) Wn=ksm(Wn,mdn-2); for(int i=0,w=1;i<lim;i+=k,w=1) for(int j=0;j<mid;j++,w=(ll)w*Wn%mdn) { int x=a[i+j],y=(ll)w*a[i+mid+j]%mdn; a[i+j]=(x+y)%mdn; a[i+j+mid]=(mdn+x-y)%mdn; } } if(f) for(int i=0;i<lim;i++) a[i]=(ll)a[i]*inv%mdn; } bool bor[N]; int a[N<<2],b[N<<2],n; char ch[N]; void sieve() { for(int i=1;i<n;i++) for(int j=i;j<n;j+=i) if(a[n-j-1]|a[n+j-1]){bor[n-i]=0;break;} } ll getans() { ll ans=0; for(int i=1;i<=n;i++) ans^=((ll)bor[i]*i*i); return ans; } int main() { memset(bor,1,sizeof(bor)); scanf("%s",ch); n=strlen(ch); for(int i=0;i<n;i++) a[i]=(ch[i]=='0'),b[i]=(ch[n-i-1]=='1'); int lim=pre(n<<1); ntt(a,lim,0); ntt(b,lim,0); for(int i=0;i<lim;i++) a[i]=(ll)a[i]*b[i]%mdn; ntt(a,lim,1); sieve(); printf("%lld ",getans()); return 0; }