【BZOJ3160】万径人踪灭-FFT+Manacher

测试地址:万径人踪灭
做法:本题需要用到FFT+Manacher。
这道题要求不连续的回文子序列数量,显然这个数量等于所有回文子序列数量减去连续的回文子序列数量,后面的部分很显然可以用Manacher算法求出,那么现在的问题就是要求出所有回文子序列数量。
对于每条对称轴,都存在一个数k,使得任意满足i+j=k的数对i,j,有ai+aj关于该对称轴对称。用[U]表示表达式U的真值(01),那么关于该对称轴对称且对应字符相同的字符对数为:
(1+i+j=k[ai=aj])/2
将字符ab转化为01,那么上面这个式子显然等于:
(1+i+j=k[ai=0][aj=0])/2+(1+i+j=k[ai=1][aj=1])/2
那么令向量A中的Ai[ai=x](x=0,1),那么上面的加号两边的和式很显然是向量卷积的形式了,用FFT优化即可O(nlogn)计算得出。
那么得到关于某对称轴对称且对应字符相同的字符对数c后,我们可以得到关于这条对称轴对称的回文子序列数目:2c1,之所以要减去1是因为我们不把空串计算在内。对于所有对称轴累加上面的值,再减去用Manacher算出的回文串数即可得到最终的答案,这样我们就解决了这一题。
以下是本人代码:

#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const double pi=acos(-1.0);
const double eps=1e-5;
int n,x,r[500010];
ll cnt[500010]={0},len[500010],ans=0;
char s[200010];
struct Complex
{
    double x,y;
}a[500010];
Complex operator + (Complex a,Complex b) {Complex s={a.x+b.x,a.y+b.y};return s;}
Complex operator - (Complex a,Complex b) {Complex s={a.x-b.x,a.y-b.y};return s;}
Complex operator * (Complex a,Complex b) {Complex s={a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};return s;}

ll power(ll a,ll b)
{
    ll s=1,ss=a;
    while(b)
    {
        if (b&1) s=(s*ss)%mod;
        ss=(ss*ss)%mod;b>>=1;
    }
    return s;
}

void FFT(Complex *a,int n,int type)
{
    for(int i=0;i<n;i++)
        if (i<r[i]) swap(a[i],a[r[i]]);
    for(int mid=1;mid<n;mid<<=1)
    {
        Complex W={cos(pi/mid),type*sin(pi/mid)};
        for(int l=0;l<n;l+=(mid<<1))
        {
            Complex w={1.0,0.0};
            for(int k=0;k<mid;k++,w=w*W)
            {
                Complex x=a[l+k],y=w*a[l+mid+k];
                a[l+k]=x+y;
                a[l+mid+k]=x-y;
            }
        }
    }
    if (type==-1)
    {
        for(int i=0;i<n;i++)
            a[i].x/=n;
    }
}

void Manacher()
{
    for(int i=n-1;i>=0;i--)
    {
        s[(i<<1)+2]='#';
        s[(i<<1)+1]=s[i];
    }
    s[0]='#';
    len[1]=1;
    int dis=1,mid=1;
    for(int i=2;i<(n<<1);i++)
    {
        if (i>dis||i+len[(mid<<1)-i]>=dis)
        {
            len[i]=max(0,dis-i);
            while(len[i]<i&&s[i+len[i]+1]==s[i-len[i]-1]) len[i]++;
            mid=i;dis=i+len[i];
        }
        else if (i+len[(mid<<1)-i]<dis) len[i]=len[(mid<<1)-i];
    }
}

int main()
{
    scanf("%s",s);
    n=strlen(s);

    memset(a,0,sizeof(a));
    for(int i=0;i<n;i++)
        if (s[i]=='a') a[i].x=1.0;

    int bit=0,x=1;
    while(x<(n<<1)) x<<=1,bit++;
    r[0]=0;
    for(int i=1;i<x;i++)
        r[i]=(r[i>>1]>>1)|((i&1)<<(bit-1));

    FFT(a,x,1);
    for(int i=0;i<x;i++)
        a[i]=a[i]*a[i];
    FFT(a,x,-1);
    for(int i=0;i<(n<<1)-1;i++)
    {
        cnt[i]+=((ll)(a[i].x+eps)+1)/2;
    }

    memset(a,0,sizeof(a));
    for(int i=0;i<n;i++)
        if (s[i]=='b') a[i].x=1.0;

    FFT(a,x,1);
    for(int i=0;i<x;i++)
        a[i]=a[i]*a[i];
    FFT(a,x,-1);
    for(int i=0;i<(n<<1)-1;i++)
    {
        cnt[i]+=((ll)(a[i].x+eps)+1)>>1;
        ans=(ans+(power(2,cnt[i])-1+mod)%mod)%mod;
    }

    Manacher();
    for(int i=1;i<(n<<1);i++)
        ans=(ans-((len[i]+1)>>1)+mod)%mod;
    printf("%lld",ans);

    return 0;
}
原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793515.html