Bzoj3160:万径人踪灭

题面

Bzoj

Sol

求不连续回文子序列的个数
ans= a n s = 回文子序列个数-连续回文子序列个数
即回文子序列个数-回文子串个数
后面直接 Manacher M a n a c h e r 就好了
考虑前面的
枚举对称轴,设 f[i] f [ i ] 表示对称轴 i i 两边相同字符的对数
那么最终答案就是2f[i]1
考虑求 f[i] f [ i ]
只有当原串中的两个字符相同才会有贡献
也就是 s[ix]=s[i+x] s [ i − x ] = s [ i + x ]
单独考虑 a a b的贡献
f[i]=[s[ix]==s[i+x]] f [ i ] = ∑ [ s [ i − x ] == s [ i + x ] ]
设当前考虑 a a 的贡献
把是a的设为 1 1 不是的为0,做个卷积就可以求出 f f
那么就可以FFT
注意两个字符之间也算对称轴

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int Zsy(1e9 + 7); 
const int _(5e5 + 5);
const double PI(acos(-1));

int n, p[_];
ll f[_];
int N, M, l, r[_];
char s[_], a[_];
struct Complex{
    double real, image;
    IL Complex(){
        real = image = 0;
    }

    IL Complex(RG double a, RG double b){
        real = a, image = b;
    }

    IL Complex operator +(RG Complex B){
        return Complex(real + B.real, image + B.image);
    }

    IL Complex operator -(RG Complex B){
        return Complex(real - B.real, image - B.image);
    }

    IL Complex operator *(RG Complex B){
        return Complex(real * B.real - image * B.image, real * B.image + image * B.real);
        }
} A[_], B[_];

IL void FFT(RG Complex *P, RG int opt){
    for(RG int i = 0; i < N; ++i) if(i < r[i]) swap(P[i], P[r[i]]);
    for(RG int i = 1; i < N; i <<= 1){
        RG Complex W(cos(PI / i), opt * sin(PI / i));
        for(RG int j = 0, p = i << 1; j < N; j += p){
            RG Complex w(1, 0);
            for(RG int k = 0; k < i; ++k, w = w * W){
                RG Complex X = P[k + j], Y = w * P[k + j + i];
                P[k + j] = X + Y, P[k + j + i] = X - Y;
            }
        }
    }
}

IL void Mul(){
    FFT(A, 1);
    for(RG int i = 0; i < N; ++i) B[i] = A[i] * A[i];
    FFT(B, -1);
    for(RG int i = 0; i < N; ++i) B[i].real = B[i].real / N + 0.5;
    for(RG int i = 1; i <= M; ++i) f[i] += ((ll)(B[i].real) + 1) >> 1;
}

IL ll Manacher(){
    RG ll ans = 0; RG int mx = 0, len = 1; a[1] = '#';
    for(RG int i = 1; i <= n; ++i) a[++len] = s[i], a[++len] = '#';
    for(RG int i = 1, id = 0, mx = 0; i <= len; ++i){
        if(i < mx) p[i] = min(mx - i, p[(id << 1) - i]);
        while(i - p[i] && i + p[i] <= len && a[i - p[i]] == a[i + p[i]]) ++p[i];
        if(p[i] + i > mx) mx = p[i] + i, id = i;
        (ans += p[i] >> 1) %= Zsy;
    }
    return ans;
}

IL ll Pow(RG ll x, RG ll y){
    RG ll ret = 1;
    for(; y; y >>= 1, x = x * x % Zsy)
        if(y & 1) ret = ret * x % Zsy;
    return ret;
}

int main(RG int argc, RG char* argv[]){
    scanf(" %s", s + 1); n = strlen(s + 1);
    for(M = n + n, N = 1; N <= M; N <<= 1) ++l;
    for(RG int i = 0; i < N; ++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
    RG ll ans = -Manacher();
    for(RG int i = 0; i < N; ++i) A[i] = Complex(s[i] == 'a', 0);
    Mul();
    for(RG int i = 0; i < N; ++i) A[i] = Complex(s[i] == 'b', 0);
    Mul();
    for(RG int i = 1; i <= M; ++i) (ans += Pow(2, f[i]) - 1) % Zsy;
    printf("%lld\n", (ans + Zsy) % Zsy);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值