[Bzoj3160]万径人踪灭

题意

给定一个由 a a b构成的字符串,求不连续回文子序列的个数


题解

不连续回文子序列数=回文子序列数-回文子串数

后面的可以直接用 PAM P A M 求出来

(我才不会说因为 Manacher M a n a c h e r 能干的 PAM P A M 都能干,所以弃掉了 Manacher M a n a c h e r )

考虑回文子序列数怎么求

如果 si=sj s i = s j 那么这两个字符就关于 i+j2 i + j 2 对称

考虑 (si,sj) ( s i , s j ) 选不选就对 i+j2 i + j 2 ×2 × 2 的贡献

所以可以设 fi+j2= f i + j 2 = 左右两边有多少对 (si,sj) ( s i , s j ) 满足 si=sj s i = s j

去掉左右的限制 fi+j2=[si=sj]2 f i + j 2 = ∑ [ s i = s j ] 2

下标里面有 12 1 2 很讨嫌,而且对称中心还可能是小数

所以直接设 fi+j=[si=sj]2 f i + j = ∑ [ s i = s j ] 2

上面看起来是一个卷积的形式,考虑把等于变成乘法

fi+j=c[si=c][sj=c]2=cS2c(x)2 ⇒ f i + j = ⌈ ∑ c ∑ [ s i = c ] ∗ [ s j = c ] 2 ⌉ = ⌈ ∑ c S c 2 ( x ) 2 ⌉

所以就可以枚举字符集,然后用做 |c|+1 | c | + 1 FFT F F T fi f i

因为 FFT F F T 里如果两个字符位置相同 (si=si) ( s i = s i ) 是不会被算两遍的,所以要向上取整

然后 x2=x+12 ⌈ x 2 ⌉ = ⌊ x + 1 2 ⌋

然后全集 Sum=2ni=12fi1 S u m = ∑ i = 1 2 n 2 f i − 1 (非空子集)

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e5+5,M=1<<18,P=1e9+7;
inline int pls(int a,int b){return a+=b,a>=P?a-P:a;}
inline int sub(int a,int b){return a-=b,a<0?a+P:a;}
struct PAM{
    int n,T,las;char s[N];
    struct node{int fail,len,cnt,ch[26];}a[N];
    inline void init(){las=n=0;T=a[0].fail=1;s[0]=a[1].len=-1;}
    inline int gf(int x){while(s[n-a[x].len-1]^s[n])x=a[x].fail;return x;}
    inline void ins(int v){
        s[++n]=v;las=gf(las);
        if(!a[las].ch[v]){
            a[++T]={a[gf(a[las].fail)].ch[v],a[las].len+2};
            a[las].ch[v]=T;
        }++a[las=a[las].ch[v]].cnt;
    }
    inline int calc(){
        int tp=0;
        fd(i,T,2)a[a[i].fail].cnt=pls(a[a[i].fail].cnt,a[i].cnt),tp=pls(tp,a[i].cnt);
        return tp;
    }
}p;
int n,K,ans,pw[M],R[M];char s[N];
struct cp{
    double x,y;
    inline cp operator+(const cp&b){return(cp){x+b.x,y+b.y};}
    inline cp operator-(const cp&b){return(cp){x-b.x,y-b.y};}
    inline cp operator*(const cp&b){return(cp){x*b.x-y*b.y,x*b.y+y*b.x};}
}a[M],b[M],w[M];
const double pi=acos(-1),eps=0.1;
inline void fft(cp*const a,const int tp){
    fp(i,0,K-1)if(i>R[i])swap(a[i],a[R[i]]);
    for(int i=2;i<=K;i<<=1){
        int d=i>>1;cp e=cp{cos(pi/d),tp*sin(pi/d)},o;
        for(int j=d;j>=0;j-=2)w[j]=w[j>>1];
        for(int j=1;j<=d;j+=2)w[j]=w[j-1]*e;
        for(cp*x=a;x!=a+K;x+=i)fp(k,0,d-1)
            o=x[k+d]*w[k],x[k+d]=x[k]-o,x[k]=x[k]+o;
    }
}
int main(){
    #ifndef ONLINE_JUDGE
        file("s");
    #endif
    scanf("%s",s);n=strlen(s);p.init();
    w[0]={1};K=1;while(K<n<<1)K<<=1;pw[0]=1;
    fp(i,1,K-1)pw[i]=pls(pw[i-1],pw[i-1]);
    fp(i,0,n-1)s[i]=='a'?a[i].x=1:b[i].x=1;
    for(int i=0,j=0;i<K;++i){R[i]=j;for(int l=K>>1;(j^=l)<l;l>>=1);}
    fft(a,1),fft(b,1);fp(i,0,K-1)a[i]=a[i]*a[i]+b[i]*b[i];fft(a,-1);
    fp(i,0,K-1)ans=pls(ans,pw[(int(a[i].x/K+eps)+1)>>1]-1);
    fp(i,0,n-1)p.ins(s[i]-'a');
    printf("%d",sub(ans,p.calc()));
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值