LOJ6436 PKUSC2018 神仙的游戏

Problem

LOJ

Solution

yasar的游戏(逃)

PKUSC2018除了主斗地终于都写完了。。先纪念一下23333

这题的难点应该是border的性质= =border的概念是在kmp的时候有提及的,就比如 n x t [ ∣ S ∣ ] nxt[|S|] nxt[S]就是原串的最长border,但这是题外话了。若存在一个长度为i的border,则说明原串存在一个 ∣ S ∣ − i |S|-i Si的循环长度,后面的则被截断了。

若存在的话就说明把原串按照循环长度分组,同组里不允许同时存在0和1。

如果存在某对01距离差为d,则循环长度不能被d整除,否则就会被分至同一组。

所以现在的问题就是统计所有的01的距离差有哪些,考虑生成函数

A ( x ) = ∑ s [ i ] = 0 x − i A(x)=\sum_{s[i]=0}x^{-i} A(x)=s[i]=0xi

B ( x ) = ∑ s [ i ] = 1 x i B(x)=\sum_{s[i]=1}x^i B(x)=s[i]=1xi

卷积一下即可得到,但是由于指数为负数不方便运算,我们把A乘上 x n x^n xn。最后扫一遍即可。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

Code

#include 
#include 
#include 
#define rg register
using namespace std;
typedef long long ll;
const int maxn=500010,mod=998244353,G=3;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int n,t,N=1,l,vis[maxn],rev[maxn<<2],a[maxn<<2],b[maxn<<2],c[maxn<<2];
ll ans;
char s[maxn];
inline int abs(int x){return x<0?-x:x;}
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int power(int x,int y)
{
	int res=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1)
	    res=(ll)res*x%mod;
	return res;
}
void ntt(int *a,int f)
{
	for(rg int i=0;i<N;i++)
	  if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1;i<N;i<<=1)
	{
		int gn=power(G,(mod-1)/(i<<1));
		for(int j=0;j<N;j+=(i<<1))
		{
			int x,y,g=1;
			for(rg int k=0;k<i;k++,g=(ll)g*gn%mod)
			{
				x=a[j+k],y=(ll)g*a[j+k+i]%mod;
				a[j+k]=pls(x,y);a[j+k+i]=dec(x,y);
			}
		}
	}
	if(f==-1)
	{
		int inv=power(N,mod-2);reverse(a+1,a+N);
		for(rg int i=0;i<N;i++) a[i]=(ll)a[i]*inv%mod;
	}
}
int main()
{
	scanf("%s",s);
	n=strlen(s);
	for(rg int i=0;i<n;i++)
	{
		if(s[i]=='0') a[n-i-1]=1;
		if(s[i]=='1') b[i]=1;
	}
	while(N<(n<<1)) N<<=1,l++;
	for(rg int i=1;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
	ntt(a,1);ntt(b,1);
	for(rg int i=0;i<N;i++) c[i]=(ll)a[i]*b[i]%mod;
	ntt(c,-1);
	for(rg int i=0;i<N;i++)
	  if(c[i])
	    vis[abs(n-i-1)]=1;
	for(rg int i=1;i<n;i++)
	{
		t=1;
		for(rg int j=(n-i);j<=n;j+=(n-i))
		  if(vis[j])
	      {t=0;break;}
	    if(t) ans^=(ll)i*i;
	}
	ans^=(ll)n*n;
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(多项式,生成函数)