zoukankan      html  css  js  c++  java
  • 【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;
    }
  • 相关阅读:
    js 多物体运动
    js运动 淡入淡出
    js运动 分享到
    正则 重复项最多得子项
    jq 拖拽
    jq 弹出窗口
    jq 选项卡
    jq 写法
    Codeforces 185A Plant( 递推关系 + 矩阵快速幂 )
    HDU 2604 Queuing( 递推关系 + 矩阵快速幂 )
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793515.html
Copyright © 2011-2022 走看看