【BZOJ3160】万径人踪灭 FFT manacher

我是真的想写字符串的题。。。

这道题除了manacher和字符串有半!毛!钱!关!系!

题目所求的数量可以由(无限制对称字符串)-(连续对称字符串)求得

其中(连续对称字符串)可以由manacher求得,问题变为求(无限制对称字符串)的数量

我们考虑d[i]表示s[a]==s[b]&&a+b==i的数的对数(ab可以相等并且ab有序),那么sum=sigma(2^( ( d[i] - 1 ) / 2))

现在的问题是如何求d[i]

首先不难想到n^2的算法,然后发现d[i]是一个卷积的形式,假设我们要求同为a的d[i],就做一个n次多项式A,a[i]= s[i]=='a'?1:0  d[i]就相当于A^2中次数为i的项的次数,因此我们可以用FFT进行优化。

#include<cstdlib>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define maxn 400005
#define pi 3.14159265358979323846264338327950388419716939937530
#define mo 1000000007
char s0[maxn],s[3*maxn];
int cnt,n,d[3*maxn];
void Init()
{
	scanf("%s",s0);
	n=strlen(s0);cnt=0;
	cnt++; s[0]='$'; s[cnt]='#';
	for(int i=0;i<n;i++)
	{
		s[++cnt]=s0[i]; s[++cnt]='#';
	}
	d[0]=0; int maxi=0;
	for(int i=1;i<=cnt;i++)
	{
		if(i<=maxi+d[maxi]-1)d[i]=min(d[2*maxi-i],maxi+d[maxi]-i);
		while(s[i+d[i]]==s[i-d[i]])d[i]++;
		if(i+d[i]>=maxi+d[maxi])maxi=i;
	}
	s[cnt+1]='\0';
/*	printf("%s\n",s+1);
	for(int i=1;i<=cnt;i++)
	{
		printf("%d",d[i]);
	}
	putchar('\n');*/
	return ;
}
struct _My_Complex
{
	double i,j;
	_My_Complex()  {}
	_My_Complex(double c,double cc) : i(c),j(cc) {}
	void operator +=(_My_Complex a)
	{
		i+=a.i; j+=a.j;
	}
	friend _My_Complex operator + (_My_Complex a, _My_Complex b)
	{
		return _My_Complex(a.i+b.i,a.j+b.j);
	}
	friend _My_Complex operator - (_My_Complex a, _My_Complex b)
	{
		return _My_Complex(a.i-b.i,a.j-b.j);
	}
	friend _My_Complex operator * (_My_Complex a, _My_Complex b)
	{
		return _My_Complex(a.i*b.i-a.j*b.j,a.i*b.j+a.j*b.i);
	}
}u,t;

_My_Complex a[maxn*2],b[maxn*2];
bool v[maxn*2];
int c;
void FFT(_My_Complex *x,int l,int on)
{

	for(int i=0;i<l;i++)
	{
		int j=0;
		for(int w=0;w<c;w++)if((1<<w)&i)
		{
			j|=(1<<(c-1-w));
		}
		if(i<j)swap(x[i],x[j]);
	}
	
	for(int k=2;k<=l;k=k*2)
	{
		_My_Complex wn(cos((double)on*2*pi/k),sin((double)on*2*pi/k));
		
		for(int j=0;j<l;j+=k)
		{
			_My_Complex w(1.0,0.0);
			for(int i=j;i<j+k/2;i++)
			{
				u=x[i]; t=x[i+k/2]*w;
				x[i]=u+t; x[i+k/2]=u-t;
				w=w*wn;
			}
			
		}
	}
	if(on==-1)
	{
		for(int i=0;i<l;i++)x[i].i=x[i].i/l;
	}
	return ;
}
int l;
int ans,B[maxn],C[maxn];
void work()
{
	B[0]=1;
	for(int i=1;i<=100000;i++)B[i]=(B[i-1]*2)%mo;
	l=1;c=0;
	while(l<n*2)l=l*2,c++;
	for(int i=0;i<n;i++)
	{
		a[i].i= s0[i]=='a'? 1.0:0.0;a[i].j=0;
	}
	for(int i=n;i<l;i++)
	{
		a[i].i= 0.0;a[i].j=0;
	}
	FFT(a,l,1);
	for(int i=0;i<l;i++)
	{
		b[i]=a[i]*a[i];
	}
	FFT(b,l,-1);
	ans=0;
	int hh;
	for(int i=0;i<l;i++)
	{
		hh=(int)(b[i].i+0.5);
		C[i]=C[i]+hh;
	}
	
	for(int i=0;i<n;i++)
	{
		a[i].i= s0[i]=='a'? 0.0:1.0;a[i].j=0;
	}
	for(int i=n;i<l;i++)
	{
		a[i].i= 0.0;a[i].j=0;
	}
	FFT(a,l,1);
	for(int i=0;i<l;i++)
	{
		b[i]=a[i]*a[i];
	}
	FFT(b,l,-1);
	for(int i=0;i<l;i++)
	{
		hh=(int)(b[i].i+0.5);
		C[i]=C[i]+hh;
	}
	for(int i=0;i<l;i++)
	{
//		printf("%d ",C[i]);
		hh=C[i];
		ans=ans+B[(hh+1)/2]-1-d[i+2]/2;
		ans=ans%mo;
	}

	ans=(ans%mo+mo)%mo;
	printf("%d\n",ans);
	return ;
}
int main()
{
	freopen("in.txt","r",stdin);
	Init();
	work();
	
	return 0;
}


 

你可能感兴趣的:(fft)