1. 程式人生 > >【bzoj3160】萬徑人蹤滅

【bzoj3160】萬徑人蹤滅

lld iostream long long ostream 數組 tdi fin geo stdin

Time Limit: 1000 ms Memory Limit: 256 MB

description

技術分享圖片

技術分享圖片

技術分享圖片


吐槽

fft除了模板以外的第一題!!高興ovo

題目好長啊。。

好吧中途腦子秀逗了一直在想怎麽用fft求卷積。。。服了我自己了qwq

正題

首先看到說回文字符串,那就。。。馬拉車?但是馬拉車只能求連續的回文串呀。。

於是我們就想能不能先求出沒有任何限制的回文串的數量(記為\(ans1\))然後再用馬拉車求出連續的回文串的數量(記為\(ans2\)),那麽最終的\(ans = ans1 - ans2\)

現在問題就變成了怎麽求沒有任何限制的回文串的數量了

我們用\(f_i\)表示以\(i\)

為中心的對稱字符的對數,那麽可以得到這樣的一條式子:
\[ f_i = \lfloor\frac{1+\sum\limits_{j}[s_{i-j}=s_{i+j}]}{2}\rfloor \]
這樣一來顯然\(ans1 = \sum\limits_{i=1}^{len}2^{f_i}-1\)(總共有\(f_i\)對,每對可以選或者不選,最後再把空的那個(就是全部都不選)減掉)

然後這題有個很妙的限制:一個字符串中只有兩種字符\(a\)或者\(b\)

那麽我們可以分別考慮\(a\)\(b\)的貢獻,然後相加得到總的貢獻(也就是那坨sigma)

我們先考慮怎麽算\(a\)的貢獻(\(b\)的話其實一樣的所以就只討論一種情況了)

用一個數組\(A\)來記錄字符串每一位的\(a\)的貢獻

對於字符串的第\(i\)位,如果說是\(a\),我們將\(A_i\)賦為\(1\),否則為\(0\)

那麽第\(i\)位和第\(j\)位這對字符串的貢獻就為\(A_i * A_j\)

所以,我們如果把\(A\)看做一個多項式的系數矩陣,\(A * A\)算出來的就是\(a\)的貢獻

多項式乘法,那顯然用FFT就好了ovo

接著用同樣的方式算出\(b\)的貢獻,再和\(a\)的貢獻相加,得到上面式子中的sigma,然後再算\(f\)就好啦

然後這題就很愉快地做完啦ovo

(不得不說用多項式乘法求貢獻那步很妙啊ovo)

#include<iostream>
#include<cstdio> #include<cstring> #include<cmath> #define MOD 1000000007 #define ll long long using namespace std; const double pi=acos(-1); const int MAXN=4*(1e5)+10; struct cmplx { double a,b; cmplx(){} cmplx(double x,double y){a=x,b=y;} friend cmplx operator + (cmplx x,cmplx y) {return cmplx(x.a+y.a,x.b+y.b);} friend cmplx operator - (cmplx x,cmplx y) {return cmplx(x.a-y.a,x.b-y.b);} friend cmplx operator * (cmplx x,cmplx y) {return cmplx(x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a);} }a[MAXN],b[MAXN]; char s[MAXN],s1[MAXN]; int f[MAXN],mx[MAXN],two[MAXN]; int rev[MAXN]; int n,m,tot,k;//k=mx ll ans; int fft(cmplx *a,int op); int manacher(char *s1,int lens); int main(){ #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); freopen("a.out","w",stdout); #endif scanf("%s",s); int len=strlen(s); for (int i=0;i<len;++i){ if (s[i]=='a') a[i]=cmplx(1,0); else a[i]=cmplx(0,0); } k=1; two[0]=1; for (int i=1;i<=2*len;++i) two[i]=two[i-1]*2%MOD; while (k<2*len) k<<=1; fft(a,1); for (int i=0;i<k;++i) b[i]=a[i]*a[i]; memset(a,0,sizeof(a)); for (int i=0;i<len;++i){ if (s[i]=='b') a[i]=cmplx(1,0); else a[i]=cmplx(0,0); } fft(a,1); for (int i=0;i<k;++i) b[i]=b[i]+a[i]*a[i]; fft(b,-1); ans=0; for (int i=2;i<=2*len;++i){ f[i]+=(ll)(b[i-2].a/k+0.5); f[i]=f[i]+1>>1; } for (int i=2;i<=2*len;++i) ans=(ans+two[f[i]]-1)%MOD; printf("%lld\n",(ans+MOD-manacher(s,len))%MOD); } int fft(cmplx *a,int op) { int step,bit=0; cmplx w_n,w,t,u; for (int i=1;i<k;i<<=1,++bit); rev[0]=0; for (int i=0;i<k;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1)); for (int i=0;i<k;++i) if (i<rev[i]) swap(a[i],a[rev[i]]); for (int step=2;step<=k;step<<=1) { w_n=cmplx(cos(2*pi/step),op*sin(2*pi/step)); for (int st=0;st<k;st+=step) { w=cmplx(1,0); for (int i=0;i<(step>>1);++i) { t=a[st+i+(step>>1)]*w; u=a[st+i]; a[st+i]=u+t; a[st+i+(step>>1)]=u-t; w=w*w_n; } } } } int manacher(char *s,int lens){ int k,p=0,ret=0,len=1; s1[0]='$'; s1[1]='#'; for (int i=0;i<lens;++i) s1[++len]=s[i],s1[++len]='#'; memset(mx,0,sizeof(mx)); for (int i=2;i<len;++i){ if (p>i) mx[i]=min(mx[k*2-i],p-i); else mx[i]=1; while (s1[i+mx[i]]==s1[i-mx[i]]) ++mx[i]; if (i+mx[i]>p) p=i+mx[i],k=i; ret=(ret+mx[i]/2)%MOD; } return ret; }

【bzoj3160】萬徑人蹤滅