BZOJ 3160

恩,VFK大大出的题,很有(du)趣(liu)的说
题目大意:
就是给你一个只含ab的字符串,计算不连续的回文子序列的个数
我们可以先求出回文子序列的个数,再减去回文子串的个数,其中后者可以用马拉车在 O(n) 时间内解决,现在的问题就是计算回文子序列的个数
我们以abaabaa为例
我们先将其写为 a0 # b1 # a2 # a3 # b4 # a5 # a6
我们考虑以第 i 个字符(或#)为中心时的情况,我们只要算出关于i对称的字符对数 k ,则答案为2k+11,现在就只需快速求出关于 i 对称的字符个数即可
可以发现,关于i对称的字符在原字符串中的序号和是 i
比如说对a2,a3间的#,他是第 5 个字符,(a0,a5),(b1,b4),(a2,a3)就是三对关于 i 的对称字符
形式化的,我们记

fi=ij=0[s[j]=s[ij]]+12
为关于 i 对称字符的个数(包括本身就为字符的情况),注意到求和部分实际上是卷积的形式,可以用FFT加速,只需分别将a,b对应位置系数赋为 1 <script type="math/tex" id="MathJax-Element-7949">1</script>即可
细节可以参考代码(玛雅,我又把模数打错调了一晚上…):

#include <bits/stdc++.h>
using namespace std;

typedef complex<double> Comp;
const int MAXN=int(1e5+5),MOD=int(1e9+7),_=1<<18;
const Comp I(0,1);
const double PI=acos(-1);
int bin[MAXN]={1};
char s[MAXN],q[MAXN<<1];
int p[MAXN<<1];

int Manacher(){
    int len=strlen(s);
    q[0]='$';q[1]='#';
    for(int i=0;i<len;i++) q[(i+1)<<1]=s[i],q[(i+1)<<1|1]='#';
    int n=(len+1)<<1; q[n]=0;
    int mx=1,id=1,ans=0;
    for(int i=1;i<n;i++){
        p[i]=mx>i?min(mx-i,p[(id<<1)-i]):1;
        while(q[i-p[i]]==q[i+p[i]]) p[i]++;
        if(p[i]+i>mx) mx=p[i]+i,id=i;
        (ans+=(p[i]>>1))%=MOD;
    }
    return ans;
}

Comp A[_],B[_],t[_];
void DFT(int n,Comp *A,int d){
    if(n==1) return;
    int n_2=n>>1;
    for(int i=0;i<n_2;i++)
        t[i]=A[i<<1],t[i+n_2]=A[i<<1|1];
    memcpy(A,t,n<<4);
    Comp *A0=A,*A1=A+n_2;
    DFT(n_2,A0,d); DFT(n_2,A1,d);
    Comp root=exp(I*(2.0*PI/n*d)),w(1);
    for(int i=0;i<n_2;i++,w*=root)
        t[i]=A0[i]+w*A1[i],t[i+n_2]=A0[i]-w*A1[i];
    memcpy(A,t,n<<4);
}

int main(){
    scanf("%s",s);
    int ans=-Manacher(),n,len=strlen(s);
    for(n=1;n<(len<<1)-1;n<<=1);
    for(int i=1;i<=(len>>1)+1;i++) bin[i]=(bin[i-1]<<1)%MOD;
    for(int i=0;i<len;i++) A[i]=s[i]=='a';
    DFT(n,A,1);
    for(int i=0;i<n;i++) B[i]=A[i]*A[i];
    memset(A,0,n<<4);
    for(int i=0;i<len;i++) A[i]=s[i]=='b';
    DFT(n,A,1);
    for(int i=0;i<n;i++) B[i]+=A[i]*A[i];
    DFT(n,B,-1);
    for(int i=0;i<n;i++) B[i]/=n;
    for(int i=0;i<n;i++)
        (ans+=bin[(int(B[i].real()+1.5))>>1]-1)%=MOD;
    printf("%d\n",(ans+MOD)%MOD);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值