PKUSC2018 神仙的游戏

PKUSC2018 神仙的游戏

题面描述

给定一个\(01\)字符串,其中有一些位置可以任意填。问该字符串每个前缀能否成为这个字符串的\(border\)

数据范围:\(n\le 500000\)

思路

首先,存在一个长度为\(len\)\(border\)等价于存在一个长度为\(n-len\)的循环节

字符串中的两个位置,\(x,y\),如果他们分别为\(0,1\)的话,这两个位置就不可能相等。

即所有的\(d|(x-y)\)的循环节都不可能。

两个数组,分别记录每一位是否是\(0\)和每一位是否是\(1\)

再把其中一个数组反过来,就可以求差值了。

卷积卷一下即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int g=3;
const int sz=5e5+7;
typedef long long ll;
int n,len,l;
ll ans;
char s[sz];
int r[sz<<2];
int c[sz<<2];
int a[sz<<2],b[sz<<2];
int qpow(int x,int y){
	int ret=1;
	for(;y;y>>=1,x=1ll*x*x%mod) if(y&1) ret=1ll*x*ret%mod;
	return ret;
}
void ntt(int *a,int len,int type){
	for(int i=0;i<len;i++) if(i<r[i]) swap(a[i],a[r[i]]);
	for(int R=1;R<len;R<<=1){
		int wn=qpow(g,(mod-1)/(2*R));
		if(type) wn=qpow(wn,mod-2);
		for(int i=0;i<len;i+=2*R){
			int w=1;
			for(int j=0;j<R;j++,w=1ll*w*wn%mod){
				int x=a[i+j],y=1ll*a[i+j+R]*w%mod;
				a[i+j]=(x+y)%mod;
				a[i+j+R]=(x-y+mod)%mod;
			}
		}
	}
	if(type==0) return;
	int t=qpow(len,mod-2);
	for(int i=0;i<len;i++) a[i]=1ll*a[i]*t%mod; 
}
int main(){
	scanf("%s",s);
	n=strlen(s);
	for(int i=0;i<n;i++){
		if(s[i]=='0') a[i]=1;
		if(s[i]=='1') b[n-i-1]=1;
	}
	len=1,l=0;
	while(len<=2*n) len<<=1,l++;
	for(int i=0;i<len;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	ntt(a,len,0);
	ntt(b,len,0);
	for(int i=0;i<len;i++) a[i]=1ll*a[i]*b[i]%mod;
	ntt(a,len,1);
	for(int i=0;i<n;i++) if(a[i]) c[n-1-i]|=1;
	for(int i=n;i<2*n;i++) if(a[i]) c[i-n+1]|=1;
	ans=1ll*n*n;
	for(int i=1;i<n;i++){
		bool flag=1;
		for(int j=1;i*j<n;j++)
		if(c[i*j]){ flag=0; break; }
		if(flag) ans=ans^(1ll*(n-i)*(n-i));
	}
	printf("%lld\n",ans);
}
posted @ 2019-12-04 15:12  霞光  阅读(212)  评论(0编辑  收藏  举报