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;
}
万径人踪灭