bzoj3160: 万径人踪灭 FFT manacher

本文详细解析了BZOJ3160: 万径人踪灭这一问题,介绍了如何通过Manacher算法与快速傅立叶变换(FFT)相结合来解决该问题。文章提供了完整的代码实现,并解释了如何计算以每个字符为中心的对称字符个数。

bzoj3160: 万径人踪灭

题目



分析

一道FFT神题。话说题目还真是会强行玩梗啊
首先需要进行转化。
不连续是一个假的条件。因为你可以把连续的用manacher算出来。所以总的答案就是子序列答案-manacher
然后先把序列变成回文串的形式。比如串 S=aba S = a b a
变成 S=xaxbxax S ′ = x a x b x a x (下标从零开始)
我们另 fi f i 表示以i为中心的的对称字符的个数,整个答案就是
i=12n+12fi1 ∑ i = 1 2 ∗ n + 1 2 f i − 1
所以我们只需要求 fi f i
我们考虑一个对称字符对答案的贡献。
假设字符 Sa=Sb S a ′ = S b ′ ,并且 a+b=2mid a + b = 2 ∗ m i d
那么 Sa,Sb S a ′ , S b ′ 会对 mid m i d 的答案产生1的贡献。
这个时候我们考虑 a,b a , b 在原串上的位置,那么有 posa=a2+1 p o s a = ⌊ a 2 ⌋ + 1 ,b同理
于是有
如果 posa+1+posb+1 p o s a + 1 + p o s b + 1 并且 Sposa=Sposb S p o s a = S p o s b 那么在 posa+posb+1 p o s a + p o s b + 1 的位置上会产生1的贡献。
我们把它写成递推的形式
2f[a+b+2]=anbn[Sa=Sb]+1 2 ∗ f [ a + b + 2 ] = ∑ a n ∑ b n [ S a = S b ] + 1
这已经近乎是一个标准卷积式,只需要将bool表达式转化,容易想到令 a=0,b=1 a = 0 , b = 1 再令 a=1,b=0 a = 1 , b = 0 ,利用FFT统计答案即可得到 f f 的值。
一个细节就是,注意卷积中a,b的答案会统计到a+b上,所以f值的位置是最终卷积系数下标减二。具体看代码。

代码

我有一份垃圾的代码,可惜这里空间太大,必须放下
FFT打爆,manacher打爆,各种爆。还是得多提升编程水平。思路。。。不存在的。。

/**************************************************************
    Problem: 3160
    User: 2014lvzelong
    Language: C++
    Result: Accepted
    Time:1620 ms
    Memory:23168 kb
****************************************************************/

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const double pi = acos(-1);
const int N = 400010, mod = 1e9 + 7;
int s[N], R[N], st[N], p[N], bin[N], f[N], n, m;
void readchar() {
    char ch = getchar();
    while(ch != 'a' && ch != 'b') ch = getchar();
    for(;ch >= 'a' && ch <= 'b'; ch = getchar()) s[n++] = ch - 'a';
}

struct cp {
    double r, i;
    cp(double real = 0, double image = 0) : r(real), i(image) {}
    cp operator * (cp a) {return cp(r * a.r - i * a.i, r * a.i + i * a.r);}
    cp operator + (cp a) {return cp(r + a.r, i + a.i);}
    cp operator - (cp a) {return cp(r - a.r, i - a.i);}
}a[N], b[N];

void FFT(cp *F, int f) {
    for(int i = 0;i < m; ++i) if(i < R[i]) swap(F[i], F[R[i]]);
    for(int i = 1;i < m; i <<= 1) {
        cp wn(cos(pi / i), f * sin(pi / i));
        for(int j = 0;j < m; j += (i << 1)) {
            cp w(1, 0);
            for(int l = 0;l < i; ++l , w = w * wn) {
                cp x = F[j + l], y = w * F[j + l + i];
                F[j + l] = x + y; F[j + l + i] = x - y;
            }
        }
    }
}
int manacher() {
    st[0] = 3; st[1] = 2; st[(n << 1) + 2] = 4;
    for(int i = 0;i < n; ++i) st[i + 1 << 1] = s[i], st[i + 1 << 1 | 1] = 2;
    int id = 0, tot = 0;
    for(int i = 1;i < (n << 1) + 1; ++i) {
        if(i < id + p[id]) p[i] = min(id + p[id] - i, p[(id << 1) - i]);
        while(st[i - p[i]] == st[i + p[i]] && i + p[i]) ++p[i];
        if(i + p[i] > id + p[id]) id = i;
        tot = (tot + p[i] / 2) % mod;
    }
    return tot;
}
void work(int p) {
    for(int i = 0;i < n; ++i) a[i] = cp(s[i] ^ p);
    FFT(a, 1);
    for(int i = 0;i < m; ++i) b[i] = b[i] + a[i] * a[i];
}
int main() {
    readchar();
    bin[0] = 1; for(int i = 1;i < N; ++i) bin[i] = (bin[i - 1] << 1) % mod;
    int L; for(m = 1, L = 0; m <= n << 1;  ++L, m <<= 1); 
    for(int i = 0;i < m; ++i) R[i] = (R[i >> 1] >> 1) | ((i & 1) << L - 1);
    work(1); memset(a, 0, sizeof(a)); work(0);
    FFT(b, -1);
    long long ans = 0;
    for(int i = 2;i < (n << 1) + 1; ++i) f[i] = (long long)(b[i - 2].r + 0.5) / m;
    for(int i = 2;i < (n << 1) + 1; ++i) ans = (ans + bin[(f[i] + 1) >> 1] - 1) % mod;
    printf("%lld\n", (ans + mod - manacher()) % mod);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值