【洛谷5466】[PKUSC2018] 神仙的游戏(FFT)

点此看题面

  • 给定一个长度为\(n\)\(01\)串,其中有一些字符缺失了。
  • 对于每一个\(i\),问是否存在一种填法使得长度为\(i\)的前缀与长度为\(i\)的后缀相同。
  • \(n\le5\times10^5\)

一年前曾开过这道题,当时有个脑瘫的想法,调了半天才发现问题,一怒之下就弃了。

一年后又开了这道题,又先有了当时那个脑瘫的想法,结果再次想了半天才重新发现问题,看来一年来实力毫无长进。。。

\(border\)的性质

对于一个长度为\(i\)的前缀,如果它与长度为\(i\)的后缀相同,我们称其为一个\(border\)

\(border\)有一个基本性质,就是长度为\(i\)的前缀是\(border\),充要于\(n-i\)是该字符串的一个周期

这个性质的证明,只要自己画下图,分前缀和后缀重叠与不重叠两类讨论一下就行了。

解题思路

考虑如果\(i\)可能成为\(border\),就是要判断\(n-i\)是否可能成为周期。

也就是说,不能存在两个\(0,1\),满足它们的下标之差是\(n-i\)的倍数。

因此我们令\(a_i\)表示\(s_i\)是否为0\(b_i\)表示\(s_{n-i+1}\)是否为1

那么只要\((a*b)[n-x+1]\not=0\)\((a*b)[n+x+1]\not=0\),就说明存在两个\(0,1\)下标之差为\(x\),则周期\(x\)非法。

然后再对于每一个可能的周期,去看一下它的倍数中是否有非法的,如果有那么它也非法了。

代码:\(O(nlogn)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define DB double
using namespace std;
int n,a[2*N+5],b[N+5],p[N+5];char s[N+5];
namespace Poly//多项式乘法
{
	int P,L,R[N<<2];DB Pi=acos(-1);
	struct node
	{
		DB x,y;I node(Con DB& a=0,Con DB& b=0):x(a),y(b){}
		I node operator + (Con node& o) Con {return node(x+o.x,y+o.y);}
		I node operator - (Con node& o) Con {return node(x-o.x,y-o.y);}
		I node operator * (Con node& o) Con {return node(x*o.x-y*o.y,x*o.y+y*o.x);}
	}A[N<<2],B[N<<2];
	I void FFT(node* s,CI op)
	{
		RI i,j,k;node U,S,x,y;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x,0);
		for(i=1;i^P;i<<=1) for(U=node(cos(Pi/i),op*sin(Pi/i)),j=0;j^P;j+=i<<1)
			for(S=1,k=0;k^i;++k,S=S*U) s[j+k]=(x=s[j+k])+(y=S*s[i+j+k]),s[i+j+k]=x-y;
	}
	I void Mul(int* a,int* b)//卷积
	{
		RI i;P=1,L=0;W(P<=(n<<1)) P<<=1,++L;for(i=0;i^P;++i) R[i]=(R[i>>1]>>1)|((i&1)<<L-1);
		for(i=1;i<=n;++i) A[i]=a[i],B[i]=b[i];FFT(A,1),FFT(B,1);
		for(i=0;i^P;++i) A[i]=A[i]*B[i];for(FFT(A,-1),i=1;i<=2*n;++i) a[i]=A[i].x/P+0.5;
	}
}
int main()
{
	RI i,j;for(scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) a[i]=s[i]=='0',b[n-i+1]=s[i]=='1';//初始化
	for(Poly::Mul(a,b),p[0]=1,i=n;i;--i)
		for(p[i]=!a[n-i+1]&&!a[n+i+1],j=i<<1;p[i]&&j<=n;j+=i) p[i]&=p[j];//如果倍数中有非法的,它也非法了
	long long t=0;for(i=1;i<=n;++i) p[n-i]&&(t^=1LL*i*i);return printf("%lld\n",t),0;//i是border充要于n-i是周期
}
posted @ 2020-12-11 20:07  TheLostWeak  阅读(25)  评论(0编辑  收藏