[BZOJ3160]萬徑人蹤滅(FFT+manacher)
阿新 • • 發佈:2019-02-13
題目描述
題目大意:在一個只含ab的字串中選取一個子序列,使得:1、字元和下標都關於一箇中心對稱2、不能是連續的一段。求方案數。
題解
這題我的方法好蠢啊→_→
首先容斥一下,答案=所有子序列的方案數-迴文子串的數量
由於迴文的連續子序列一定滿足下標對稱,所以可以直接用manacher求出迴文子串的數量
然後就是統計所有子序列的方案數的問題了
將a和b分開考慮,f(i)和g(i)表示當前這一位是否是當前這個字元,非1即0。然後還是像manacher一樣在兩個字串中插入分割符的話,對於一個迴文中心i,令F(2i)表示迴文中心為i的最長迴文子序列長度,那麼 j)g(2i−j)
求出以每一個點為迴文中心的最長迴文子序列長度了之後F(2i),就是計算方案數的問題,容易知道每一個迴文中心的貢獻是
但是這個方法確實蠢極了!!實際上,滿足迴文的子序列一定滿足對應的元素下標之和為一個定值,無論長度為奇數還是偶數!所以直接一個最簡單的式子
程式碼
#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);
}