• BZOJ 3160 万径人踪灭


    Em 很长很长很啰嗦的题目

    大意就是给定一个字符串,求有多少子序列关于某条对称轴对称,且子序列不连续

    首先我们不考虑不连续这个限制

    那么对于任意对称轴我们只需要求出关于该对称轴对称的字符有多少个

    设其为C[i]个,那么这条对称轴产生的贡献是2^C[i]-1

    容易发现对称的实质是对于任意点i 存在A[i-n]=A[i+n] (这样说并不严谨,意会。。

    注意到i-n+i+n=2*i,是个卷积形式

    那么我们设‘b'=1,’a'=0,做一遍FFT

    之后再设‘b'=0,’a'=1,再做一遍FFT

    那么C[i]就计算出来了

    至于不连续的这个限制,我们只需要用总方案-连续的即可

    连续的就是求有多少个回文子串了

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    typedef long long LL;
    const int maxn=400010;
    const int mod=1000000007;
    const double pi=acos(-1.0);
    int n;
    int Num[32],cur,N,L;
    int ch[maxn],tot,len,la;
    int C[maxn],end[maxn];
    int rev[maxn];
    char s[maxn];
    LL ans;
    struct Node{
        int len,fa,next[2];
    }t[maxn];
    struct cpx{
        double r,i;
        cpx(double r=0,double i=0):r(r),i(i){}
    }A[maxn],tmp[maxn],x,y;
    cpx operator +(const cpx &a,const cpx &b){return cpx(a.r+b.r,a.i+b.i);}
    cpx operator -(const cpx &a,const cpx &b){return cpx(a.r-b.r,a.i-b.i);}
    cpx operator *(const cpx &a,const cpx &b){return cpx(a.r*b.r-a.i*b.i,a.r*b.i+a.i*b.r);}
    void init(){
        ch[0]=-1;len=la=0;tot=1;
        t[0].fa=t[1].fa=1;
        t[0].len=0;t[1].len=-1;
        for(N=1;N<n;N<<=1,L++);N<<=1,L++;
        for(int i=0;i<N;++i){
            cur=0;
            for(int t=i;t;t>>=1)Num[++cur]=(t&1);
            for(int j=1;j<=L;++j)rev[i]=(rev[i]<<1)+Num[j];
        }return;
    }
    int Get_fa(int x){
        while(ch[len-t[x].len-1]!=ch[len])x=t[x].fa;
        return x;
    }
    void add(int c){
        ch[++len]=c;
        int tmp=Get_fa(la);
        if(!t[tmp].next[c]){
            ++tot;
            t[tot].len=t[tmp].len+2;
            t[tot].fa=t[Get_fa(t[tmp].fa)].next[c];
            t[tmp].next[c]=tot;
            //cout<<tot<<' '<<t[tot].fa<<endl;
            //system("pause");
        }la=t[tmp].next[c];end[la]++;
    }
    void Get_PAM(){
        for(int i=0;i<n;++i)add(s[i]-'a');
        for(int i=tot;i>=1;--i){
            end[t[i].fa]+=end[i];
            ans=ans-end[i];
        }ans%=mod;return;
    }
    LL pow_mod(LL v,LL p){
        LL tmp=1;
        while(p){
            if(p&1)tmp=tmp*v%mod;
            v=v*v%mod;p>>=1;
        }return tmp;
    }
    void FFT(cpx *A,int n,int type){
        for(int i=0;i<n;++i)tmp[i]=A[rev[i]];
        for(int i=0;i<n;++i)A[i]=tmp[i];
        for(int i=2;i<=n;i<<=1){
            cpx wn(cos(2*pi/i),sin(2*pi/i)*type);
            for(int j=0;j<n;j+=i){
                cpx w(1,0);
                for(int k=0;k<(i>>1);k++){
                    x=A[k+j];y=A[k+j+(i>>1)]*w;
                    A[k+j]=x+y;A[k+j+(i>>1)]=x-y;
                    w=w*wn;
                }
            }
        }
        if(type==-1)for(int i=0;i<n;++i)A[i].r/=n;
    }
    void Get_ans(char c){
        for(int i=0;i<N;++i)A[i]=cpx();
        for(int i=0;i<n;++i)if(s[i]==c)A[i].r=1.0;
        FFT(A,N,1);
        for(int i=0;i<N;++i)A[i]=A[i]*A[i];
        FFT(A,N,-1);
        for(int i=0;i<N;++i)C[i]=C[i]+(int)(A[i].r+0.5);
    }
    int main(){
        scanf("%s",s);
        n=strlen(s);
        init();Get_PAM();
        //cout<<"-1"<<endl;
        Get_ans('a');Get_ans('b');
        for(int i=0;i<N;++i){
            ans=ans+pow_mod(2LL,(C[i]+1)>>1)-1;
            ans%=mod;
        }
        printf("%lld
    ",(ans+mod)%mod);
        return 0;
    }
    万径人踪灭
  • 相关阅读:
    VUE笔记-如何处理vue create demo时候,不能使用上下按键选择?
    帝国CMS之PC端上新栏目后,移动端无法同步,添加内容编辑页打开空白的处理方法
    帝国cms:迁移站点后,配置多端访问显示“访问端目录不存在”
    如何批量删除帝国CMS中同一前缀的数据表?
    宝塔插件"网站监控报表"错误日志显示大量不存在的链接,处理方法及流程
    mysql删除重复数据只保留一条
    VirtualBox 中 discuzq不能添加软链接的处理方法
    mysql8 source 导入大文件时 经常意外中断 且无法再链接断续 解决方法先设置 set names utf8;
    discuzq Virtualbox 虚拟机 在共享文件夹设置软链接 in 报错 Protocol error问题
    是的,奈学教育一周年了!
  • 原文地址:https://www.cnblogs.com/joyouth/p/5333139.html
Copyright © 2020-2023  润新知