1. 程式人生 > >[BZOJ3160]萬徑人蹤滅(FFT+manacher)

[BZOJ3160]萬徑人蹤滅(FFT+manacher)

題目描述

傳送門

題目大意:在一個只含ab的字串中選取一個子序列,使得:1、字元和下標都關於一箇中心對稱2、不能是連續的一段。求方案數。

題解

這題我的方法好蠢啊→_→
首先容斥一下,答案=所有子序列的方案數-迴文子串的數量
由於迴文的連續子序列一定滿足下標對稱,所以可以直接用manacher求出迴文子串的數量
然後就是統計所有子序列的方案數的問題了
將a和b分開考慮,f(i)和g(i)表示當前這一位是否是當前這個字元,非1即0。然後還是像manacher一樣在兩個字串中插入分割符的話,對於一個迴文中心i,令F(2i)表示迴文中心為i的最長迴文子序列長度,那麼F(2i)=jf(

j)g(2ij),是一個卷積的形式,可以兩遍FFT求出然後求和
求出以每一個點為迴文中心的最長迴文子序列長度了之後F(2i),就是計算方案數的問題,容易知道每一個迴文中心的貢獻是2F(2i)1

但是這個方法確實蠢極了!!實際上,滿足迴文的子序列一定滿足對應的元素下標之和為一個定值,無論長度為奇數還是偶數!所以直接一個最簡單的式子F(i)=jf(ij)g(j)就可以解決了。。這裡的F(i)的含義應該是對應元素下標之和為i的迴文子序列最長長度

程式碼

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio> #include<cmath> using namespace std; #define N 1000005 #define Mod 1000000007 const double pi=acos(-1.0); int lss,ls,p[N],mi[N],F[N],n,m,L,R[N],ans; char s[N],ss[N]; struct complex { double x,y; complex(double X=0,double Y=0) { x=X,y=Y; } }a[N],b[N]; 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 manacher() { int id=0,mx=0; for (int i=1;i<ls;++i) { if (mx>i) p[i]=min(p[2*id-i],mx-i); else p[i]=1; while (s[i-p[i]]==s[i+p[i]]) ++p[i]; if (i+p[i]>mx) { mx=i+p[i]; id=i; } } int ans=0; for (int i=1;i<=ls;++i) { if (p[i]==1) continue; ans+=(p[i]-2)/2+1; ans%=Mod; } return ans; } void FFT(complex a[N],int opt) { for (int i=0;i<n;++i) if (i<R[i]) swap(a[i],a[R[i]]); for (int k=1;k<n;k<<=1) { complex wn=complex(cos(pi/k),opt*sin(pi/k)); for (int i=0;i<n;i+=(k<<1)) { complex w=complex(1,0); for (int j=0;j<k;++j,w=w*wn) { complex x=a[i+j],y=w*a[i+j+k]; a[i+j]=x+y,a[i+j+k]=x-y; } } } } void calc() { FFT(a,1);FFT(b,1); for (int i=0;i<=n;++i) a[i]=a[i]*b[i]; FFT(a,-1); for (int i=0;i<=n;++i) F[i]+=(int)(a[i].x/n+0.5); } int main() { scanf("%s",ss);lss=strlen(ss);s[ls++]='*'; for (int i=0;i<lss;++i) s[ls++]='#',s[ls++]=ss[i]; s[ls++]='#'; ans-=manacher(); m=ls+ls-2; for (n=1;n<=m;n<<=1) ++L; for (int i=0;i<n;++i) R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)); for (int i=0;i<=n;++i) a[i]=complex(0,0),b[i]=complex(0,0); for (int i=0;i<ls;++i) a[i].x=b[i].x=(s[i]=='a')?1:0; calc(); for (int i=0;i<=n;++i) a[i]=complex(0,0),b[i]=complex(0,0); for (int i=0;i<ls;++i) a[i].x=b[i].x=(s[i]=='b')?1:0; calc(); mi[0]=1;for (int i=1;i<=ls;++i) mi[i]=(mi[i-1]<<1)%Mod; for (int i=0;i<ls;++i) { int cnt=F[i<<1]; if (!cnt) continue; ans+=mi[(cnt-1)/2+1]-1;ans%=Mod; } ans=(ans+Mod)%Mod; printf("%d\n",ans); }