1. 程式人生 > >FFT+manacher--bzoj3160: 萬徑人蹤滅

FFT+manacher--bzoj3160: 萬徑人蹤滅

傳送門

一道隱蔽的 F F T FFT

題目要求迴文子序列的個數,不能連續的話就用全部的減去連續的,連續的可以用 m a n

a c h e r manacher 計算,全部的迴文子序列怎麼求呢?
因為這個序列中只有 a a
b b ,假設 a = 1 , b =
0 a=1,b=0
,那麼兩個位置的字元都是 a a 就是相乘為 0 0 b b 的反著來同理)。
這樣的話以 i i 為中心的迴文子序列就可以看成 s [ i k ] × s [ i + k ] = 1 s[i-k]\times s[i+k]=1 這樣的形式,兩邊的下標相加為定值,就可以使用 F F T FFT 來實現。
也就是傳說中的“我卷我自己”。

算答案的時候 i i 位置的值就是 i 2 \frac{i}{2} 位置為中心的對稱相等的個數,假設為 x x ,那麼這個位置答案就是 2 x + 1 2 1 2^{\frac{x+1}{2}}-1 ,所有位置相加再減去 m a n a c h e r manacher 求出的迴文子串個數就是最後的答案

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 400005
#define int long long
using namespace std;
const int mod=1e9+7;
const double Pi=acos(-1.0);

struct complex{
	double x,y;
	complex(double xx=0,double yy=0){x=xx,y=yy;}
}a[maxn],b[maxn];
complex operator +(complex a,complex b){return complex(a.x+b.x,a.y+b.y);}
complex operator -(complex a,complex b){return complex(a.x-b.x,a.y-b.y);}
complex operator *(complex a,complex b){return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}

int n,limit=1,l,rev[maxn],bin[maxn],len[maxn],ans;
char s[maxn],tmp[maxn];

void FFT(complex *F,int type){
	for(int i=0;i<limit;i++)
		if(i<rev[i]) swap(F[i],F[rev[i]]);
	for(int mid=1;mid<limit;mid<<=1){
		complex Wn(cos(Pi/mid),1.0*type*sin(Pi/mid));
		for(int r=mid<<1,j=0;j<limit;j+=r){
			complex w(1,0);
			for(int k=0;k<mid;k++,w=w*Wn){
				complex x=F[j+k],y=w*F[j+mid+k];
				F[j+k]=x+y,F[j+mid+k]=x-y;
			}
		}
	}
}

inline void prework(){
	for(int i=0;i<n;i++)
		if(s[i]=='a') a[i].x=1,b[i].x=0;
		else b[i].x=1,a[i].x=0;
	while(limit<2*n) limit<<=1,l++;
	for(int i=0;i<limit;i++)
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
	bin[0]=1;
	for(int i=1;i<=n;i++) bin[i]=(bin[i-1]<<1)%mod;
}

inline int init(char *p){
	tmp[0]='@';
	for(int i=1;i<=2*n;i+=2)
		tmp[i]='#',tmp[i+1]=p[i/2];
	tmp[2*n+1]='#'; tmp[2*n+2]='$';
	return 2*n+1;
}

inline int manacher(char *p,int m){
	int mx=0,pos=0,tot=0;
	for(int i=1;i<=m;i++){
		if(mx>i) len[i]=min(mx-i,len[2*pos-i]);
		else len[i]=1;
		while(p[i-len[i]]==p[i+len[i]]) len[i]++;
		if(len[i]+i>mx) mx=len[i]+i,pos=i;
		tot+=(len[i]/2)%mod;
	} return tot;
}

signed main(){
	scanf("%s",s); n=strlen(s);
	prework();
	FFT(a,1); FFT(b,1);
	for(int i=0;i<=limit;i++) a[i]=a[i]*a[i],a[i]=a[i]+b[i]*b[i];
	FFT(a,-1);
	for(int i=0;i<=2*n-2;i++){
		int xx=(int)((a[i].x+0.5)/limit);
		xx=(xx+1)>>1;
		(ans+=bin[xx]-1)%=mod;
	}
	int m=init(s); ans-=manacher(tmp,m)%mod;
	printf("%d\n",(ans+mod)%mod);
	return 0;
}