[BZOJ 3160]萬徑人蹤滅(FFT+Manacher)

Description


Problem 3160 萬徑人蹤滅
這題的題面…好長,又是圖片,這裏就不放了
太長不看版:
給出一個全部由’a’和’b’組成的字符串,求不連續 迴文 子序列(?這個應該也許不能叫子串)的方案數
如給出’aba’,’a a’就是一個符合條件的方案,而’aba’不是

Solution


當做是FFT入門題吧
爲了做這道題還學了一些新姿勢
好不容易弄清思路,又自己推了推細節上的問題,寫出來然而WA了好幾遍,後來找了個標程(不要打我)和自己對拍,發現小數據沒什麼問題,大數據掛了
猜測是取模的問題啊Orz
果然取模有問題,改了改交上去又跪,發現某個地方沒開long long,啊,AC了,感動

把a看做1,b看做0,做一次fft求卷積,可以得到關於每個位置對稱的a的個數(對稱軸可以是一個數也可以是兩個數的中間)
如圖是’abaabaa’
Step 1
像最右邊的這個2是由1*1+1*1得到的,相當於它兩側的位置上的數都會被重複的加一遍,所以之後還需要把這個問題處理掉
Step 2

然後把b看做1,a看做0,再做一遍

關於某個位置對稱的可非連續迴文半徑x 對答案的貢獻是2x1 (要問爲什麼的話,感覺這裏寫的比較清楚QvQ http://blog.csdn.net/wzq_qwq/article/details/48173545

但是這個答案是可連續可不連續的方案數,於是再用Manacher算出(連續的)迴文子串數,減掉就好了
(Manachar我其實是現學的…https://segmentfault.com/a/1190000003914228

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define Min(a,b) (a<b?a:b)
#define Max(a,b) (a>b?a:b)
#define pi acos(-1.0)
#define MAXN 400005
#define Mod 1000000007
using namespace std;
struct cp
{
    double r,i;
    cp(double r=0,double i=0):r(r),i(i){}
    cp operator + (const cp& x)
    {return cp(r+x.r,i+x.i);}
    cp operator - (const cp& x)
    {return cp(r-x.r,i-x.i);}
    cp operator * (const cp& x)
    {return cp(r*x.r-i*x.i,r*x.i+i*x.r);}
};
char s[MAXN];
long long Pow(long long x,long long n)
{
    long long ans=1;
    while(n>0)
    {
        if(n&1)
        {
            ans*=x;
            ans%=Mod;
        }
        x=(x*x)%Mod;
        n>>=1;
    }
    return ans;
}
void brc(cp *x,int l)
{
    for(int i=1,j=l/2;i<l-1;i++)
    {
        if(i<j)swap(x[i],x[j]);
        int k=l/2;
        while(j>=k)
        {
            j-=k;
            k/=2;
        }
        if(j<k)j+=k;
    }
}
void fft(cp *x,int l,int on)
{
    brc(x,l);
    for(int h=2;h<=l;h<<=1)
    {
        cp wn(cos(on*2*pi/h),sin(on*2*pi/h));
        for(int j=0;j<l;j+=h)
        {
            cp w(1,0);
            for(int k=j;k<j+h/2;k++)
            {
                cp u=x[k];
                cp t=w*x[k+h/2];
                x[k]=u+t;
                x[k+h/2]=u-t;
                w=w*wn;
            }
        }
    }
    if(on==-1)for(int i=0;i<l;i++)x[i].r/=l;
}
int rl[MAXN];
long long manachar(char *s,int l)
{
    memset(rl,0,sizeof(rl));
    char s1[MAXN*2];
    int Maxright=-1,pos=0;
    long long res=0;
    for(int i=0;i<l;i++)
    {
        s1[i*2]='#';
        s1[i*2+1]=s[i];
    }
    s1[l*2]='#';
    l=l*2+1;
    for(int i=0;i<l;i++)
    {
        if(i<Maxright){
            rl[i]=Min(rl[2*pos-i],Maxright-i);
        }
        else rl[i]=1;
        while(i-rl[i]>=0&&i+rl[i]<l&&s1[i-rl[i]]==s1[i+rl[i]])
        rl[i]++;
        if(i+rl[i]-1>Maxright)
        {
            Maxright=i+rl[i]-1;
            pos=i;
        }
        res+=(rl[i]/2)%Mod;
        res%=Mod;
    }
    return res;
}
cp x1[MAXN],x2[MAXN];
int main()
{
    scanf("%s",s);
    int l=strlen(s),L=1;
    long long ans=0;
    while(L<l*2)L<<=1;
    for(int i=0;i<l;i++)
    {
        if(s[i]=='a')x1[i]=cp(1,0);
        else x1[i]=cp(0,0);
    }
    for(int i=l;i<L;i++)
    x1[i]=cp(0,0);
    fft(x1,L,1);
    for(int i=0;i<L;i++)x1[i]=x1[i]*x1[i];
    fft(x1,L,-1);

    for(int i=0;i<l;i++)
    {
        if(s[i]=='b')x2[i]=cp(1,0);
        else x2[i]=cp(0,0);
    }
    for(int i=l;i<L;i++)
    x2[i]=cp(0,0);
    fft(x2,L,1);
    for(int i=0;i<L;i++)x2[i]=x2[i]*x2[i];
    fft(x2,L,-1);

    for(int i=0;i<L;i++)
    {
        int t=(int)(x1[i].r+x2[i].r+0.5);
        t=(t+1)/2;
        ans+=(Pow(2,t)-1)%Mod;
        ans%=Mod;
    }

    ans-=manachar(s,l);
    ans+=Mod;
    ans%=Mod;
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章