BZOJ3160 万径人踪灭 【fft + manacher】

题解

此题略神QAQ

orz po神牛

由题我们知道我们要求出:

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

我们记为ans1和ans2

ans2可以用马拉车轻松解出,这里就不赘述了

问题是ans1

我们设\(f[i]\)表示以i位置为中心的对称的字符对数,那么i位置产生的回文子序列数 = \(2^{f[i]} - 1\)

如何求?

由对称的性质,以i为对称中心的两点\(a,b\)满足\(a+b=2*i\)

我们可以设一个这样的序列:

\(c[n]\)表示以\(n/2\)位置为对称点的对称点对数【n/2若不为整数则对称中心是字符间隙】

那么有:

\(c[n] = \sum a[k]*a[n - k]\),a[k]表示k位置的字符,*运算满足当且仅当两者字符相等时为1,否则为0

我们只需要求两次fft:

①‘a‘位置赋值0,‘b‘位置赋值1,求\(c[n] = \sum a[k]*b[n - k]\)

②‘a‘位置赋值1,‘b‘位置赋值0,求\(c[n] = \sum a[k]*b[n - k]\)

两次之和即为所求,再跑一次DFT即可【我也不知道为什么可以这样,抄po神的代码

【讲道理分开来求,然后相加应该也行】

最后ans = ans1 - ans2

真心心累。。。

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<complex>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
using namespace std;
const int maxn = 800005,maxm = 200005,INF = 1000000000,P = 1000000007;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();}
    return out * flag;
}
char s[maxm],t[maxm];
int RL[maxm],n;
LL ans1,ans2,F,power[maxn];
void manacher(){
    s[0] = ‘*‘;
    int pos = 1,mr = 1; RL[1] = 1;
    for (int i = 2; i < n; i++){
        if (i <= mr) RL[i] = min(RL[2 * pos - i],mr - i + 1);
        else RL[i] = 1;
        while (s[i + RL[i]] == s[i - RL[i]]) RL[i]++;
        if (i + RL[i] - 1 >= mr) mr = i + RL[i] - 1,pos = i;
    }
}
const double pi = acos(-1);
typedef complex<double> E;
E a[maxn],b[maxn];
int m,L,R[maxn];
void fft(E* a,int f){
    for (int i = 0; i < n; i++) if (i < R[i]) swap(a[i],a[R[i]]);
    for (int i = 1; i < n; i <<= 1){
        E wn(cos(pi / i),f * sin(pi / i));
        for (int j = 0; j < n; j += (i << 1)){
            E w(1,0);
            for (int k = 0; k < i; k++,w *= wn){
                E x = a[j + k],y = w * a[j + k + i];
                a[j + k] = x + y; a[j + k + i] = x - y;
            }
        }
    }
    if (f == -1) for (int i = 0; i < n; i++) a[i] /= n;
}
int main(){
    scanf("%s",t + 1); int len = strlen(t + 1);
    for (int i = 1; i <= len; i++) s[++n] = ‘#‘,s[++n] = t[i]; s[++n] = ‘#‘;
    manacher();
    for (int i = 1; i <= n; i++) ans2 = (ans2 + (RL[i] >> 1)) % P;
    //cout<<ans2<<endl;
    power[0] = 1; for (int i = 1; i <= n; i++) power[i] = (power[i - 1] << 1) % P;
    n = len;
    m = n << 1; for (n = 1; n <= m; n <<= 1) L++;
    for (int i = 0; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
    for (int i = 1; i <= len; i++) a[i] = (t[i] == ‘a‘);
    fft(a,1);
    for (int i = 0; i < n; i++) b[i] = a[i] * a[i];
    memset(a,0,sizeof(a));
    for (int i = 1; i <= len; i++) a[i] = (t[i] == ‘b‘);
    fft(a,1);
    for (int i = 0; i < n; i++) b[i] += a[i] * a[i];
    fft(b,-1);
    for (int i = 1; i < n; i++){
        F = (LL)(b[i].real() + 0.5);
        ans1 = (ans1 + power[F + 1 >> 1] - 1) % P;
    }
    //cout<<ans1<<endl;
    printf("%lld\n",((ans1 - ans2) % P + P ) % P);
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/8352976.html

时间: 2024-10-09 15:29:35

BZOJ3160 万径人踪灭 【fft + manacher】的相关文章

BZOJ 3160 万径人踪灭 FFT+Manacher

题意:链接 方法: FFT+Manacher 解析: 对于一个序列,求以任意位置(可以为间隙)为轴对称的不连续回文序列. 我们不妨举一个栗子. 然后我们发现,如果以图中的红线为对称轴的话,那么他的最长回文长度为3,也就是可非连续回文半径为3. 所以大情况下这个对答案的贡献是 C13+C23+C33=23?1 但是其中如图所示有一些不合法的选取. 也就是对于轴来说我们选取了连续,无断点的一段. 我们要把这部分减掉. 然而这部分恰好是以红线为轴的最长连续回文串的半径. 捋一下思路. 首先我们求出所有

BZOJ3160 万径人踪灭 字符串 多项式 Manachar FFT

原文链接http://www.cnblogs.com/zhouzhendong/p/8810140.html 题目传送门 - BZOJ3160 题意 给你一个只含$a,b$的字符串,让你选择一个子序列,使得: $1.$位置和字符都关于某一条对称轴对称. $2.$不能是连续的一段. 问原来的字符串中能找出多少个这样的子序列.答案对$10^9+7$取模. 串长$\leq 10^5$. 题解 下面的讨论都在满足条件$1$的情况下进行. 首先,我们先不考虑条件$2$.然后再减掉不满足条件$2$的就可以了

万径人踪灭(FFT+manacher)

传送门 这题--我觉得像我这样的菜鸡选手难以想出来-- 题目要求求出一些子序列,使得其关于某个位置是对称的,而且不能是连续一段,求这样的子序列的个数.这个直接求很困难,但是我们可以先求出所有关于某个位置对称的子序列,最后减去子串的个数. 子串个数可以用\(manacher\)求,至于子序列的话,我们假设以第\(i\)位为中心,那么如果两边有\(x\)对相同的字符,那么这个位置对答案的贡献就是\(2^x-1\)或者\(2^(x+1)-1\).(因为有可能回文串的长度是偶数,也就是不存在中间点) 考

【bzoj3160】万径人踪灭 FFT

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3160 我是一个傻叉 微笑脸 1 #include<bits/stdc++.h> 2 #define inf 1000000000 3 #define ll long long 4 #define N 200005 5 #define mod 1000000007 6 using namespace std; 7 int read(){ 8 int x=0,f=1;char ch=getch

BZOJ3160万径人踪灭

题解: 题意即求不连续但间隔长度对称的回文串个数. 若si=sj,则这对字符可以作为以(i+j)/2为中心的回文串的一部分. 用F[i]来表示可以做为以i/2为中心的回文串的一部分的字符对数,则以i/2为中心的回文串数为2^F[i]. 则这就成了多项式乘法:先做一次a的,把字符为a的位置值赋为1,其余为0,进行一次FFT:同理做一次b的. 因为完全连续是不可以的,所以用Manacher求出这样的回文串的个数并减去. 代码: (BZOJ上PASCAL跑得不够快,再加上这题时限只有10s,并没有AC

[bzoj3160]万径人踪灭

Manacher+FFT,这题太精妙了,我现在才写是不是太弱了... Ans=以某个轴为中心的每一种回文子序列-所有连续回文串方案数 连续部分可以用Manacher在O(n)时间内解决. 第一部分:令f[i]=以i为中轴的对称对数,则对(2^f[i])-1求和即可(不能光有一根轴) 长串中i左右对称的对数位置相加一定是i(在短串中),那么f[i]=[sigma((str[x]==str[i-x])(x<i))+1]/2 这时题目还有个条件哟-串只是由a,b构成的,我们考虑一个卷积的形式C[k]=

关于万恶的多项式

模板: 1 //Achen 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdlib> 6 #include<vector> 7 #include<cstdio> 8 #include<queue> 9 #include<cmath> 10 #include<set> 11 #include&l

目前的字符串

Aho-Corasick automaton:BZOJ4820[bzoj2754][SCOI2012]喵星球上的点名后缀数组:BZOJ4310 跳蚤manacher: BZOJ3160万径人踪灭Palindromic Tree:bzoj4044Bzoj3676:[Apio2014]回文串 bzoj 1559 ** ac+jvchengBZOJ1195:[HNOI2006]最短母串 ** ac+jvchengbzoj 1692 * sabzoj 1031 * sabzoj 3796 ** sa+k

字符串题表

已完成bzoj1559 ** ac+jvchengbzoj1195:[HNOI2006]最短母串 ** ac+jvchengbzoj1692 * sabzoj1031 * sabzoj3796 ** sa+kmpbzoj3230:相似子串 ** sa+stbzoj4698 *** sabzoj2160:拉拉队排练 * manacherbzoj3790 * manacher+tanxinbzoj2565:最长双回文串 * manacher+luangaobzoj1414:[ZJOI2009]对称的