bzoj3160 万径人踪灭

http://www.elijahqi.win/archives/3096
题目在上方链接
Description

Input

Output

Sample Input

Sample Output

HINT

Source

2013湖北互测week1

首先将字符串中间插入”#” 把他们分隔开

题目要求求不连续的回文串的个数 那么就用总数减去连续的即可 考虑连续的部分 直接用manacher求即可 算出长度 然后再/2就是答案

不连续的怎么办 考虑现在用#将他们隔开 然后假如只考虑字符是a的贡献

那么 显然字符a都会出现在偶数位置上 那么想办法知道一个位置左右对称的符合要求的字符即可得到答案 比如#a#b#a这样关于b左右对称的就是2 然后答案就是2^2-1

那么想办法计算出左右对称位置符合要求的这个t即可

设当前在第k位

f[k]=i[kimod2=0][ski=a][sk+i=a] f [ k ] = ∑ i 不 越 界 [ k − i mod 2 = 0 ] [ s k − i = ′ a ′ ] [ s k + i = ′ a ′ ] ti[ki=2×t][ski2=a][sk+i2=a] ∑ t ∑ i 不 越 界 [ k − i = 2 × t ] [ s k − i 2 = ′ a ′ ] [ s k + i 2 = ′ a ′ ] ti[ki=2×t]stskt ∑ t ∑ i 不 越 界 [ k − i = 2 × t ] s t ∗ s k − t t[k2t>=0]stskt ∑ t [ k − 2 ∗ t >= 0 ] s t ∗ s k − t t=0k2stskt ∑ t = 0 ⌊ k 2 ⌋ s t ∗ s k − t s即表示该串这个位置是否为a|b为的话就是1

然后两次dft一次idft即可

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define pi acos(-1)
#define ll long long
using namespace std;
const int mod=1000000007;
const int N=100020;
struct C{
    double a,b;
    inline friend C operator +(const C &x,const C &y){return (C){x.a+y.a,x.b+y.b};}
    inline friend C operator -(const C &x,const C &y){return (C){x.a-y.a,x.b-y.b};}
    inline friend C operator *(const C &x,const C &y){return (C){x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a};}
    inline void operator *=(const C &y){*this=*this*y;}
}a[N<<2],b[N<<2];
int n,m,R[N<<2],p[N<<1];ll ans;
inline void fft(C *x,int f){
    for (int i=0;i<n;++i) if (i<R[i]) swap(x[i],x[R[i]]);
    for (int i=1;i<n;i<<=1){
        C wn=(C){cos(pi/i),f*sin(pi/i)};
        for (int j=0;j<n;j+=i<<1){
            C w=(C){1,0},t1,t2;
            for (int k=0;k<i;++k,w*=wn)
                t1=x[j+k],t2=x[j+k+i]*w,x[j+k]=t1+t2,x[j+k+i]=t1-t2;
        }
    }if (f==-1) for (int i=0;i<n;++i) x[i].a/=n;
}
inline int ksm(ll b,int t){static ll tmp;tmp=1;
    for (;t;b=b*b%mod,t>>=1) if (t&1) tmp=tmp*b%mod;return tmp;
}char s[N],s1[N<<1];
template<class T>
inline void add(T &x,int v){x=x+v>=mod?x+v-mod:x+v;}
template<class T>
inline void dec(T &x,int v){x=x-v<0?x-v+mod:x-v;}
int main(){
    freopen("bzoj3160.in","r",stdin);
    scanf("%s",s+1);m=strlen(s+1);m<<=1;
    int t=0;for (n=1;n<=m;n<<=1,++t);m>>=1;
    for (int i=0;i<n;++i) R[i]=(R[i>>1]>>1)|((i&1)<<t-1);
    for (int i=1;i<=m;++i) a[i].a=s[i]=='a',b[i].a=s[i]=='b';
    fft(a,1);
//  for (int i=0;i<n;++i) printf("%f\n",a[i].a);
    fft(b,1);for (int i=0;i<n;++i) a[i]=a[i]*a[i],b[i]=b[i]*b[i],a[i]=a[i]+b[i];
    fft(a,-1);for (int i=1;i<=m;++i) s1[i*2-1]='#',s1[i<<1]=s[i];m<<=1;int mx=0,id=0;
    for (int i=2;i<=m;++i) add(ans,ksm(2,((int)(a[i].a+0.1)+1>>1))-1);s1[++m]='#';
//  for (int i=1;i<=m;++i) printf("%lf\n",a[i].a);
//  for (int i=2;i<=m;++i) printf("%d\n",(int)(a[i].a+0.1)>>1);
    for (int i=1;i<=m;++i){
        if (i<mx) p[i]=min(mx-i,p[(id<<1)-i]);else p[i]=1;
        while(i-p[i]>0&&i+p[i]<=m&&s1[i-p[i]]==s1[i+p[i]]) ++p[i];
        if(i+p[i]>mx) mx=i+p[i],id=i;dec(ans,p[i]>>1);
    }
//  for (int i=1;i<=m;++i) printf("%d ",p[i]); 
    printf("%lld\n",ans);
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值