1. 程式人生 > >BZOJ3160 萬徑人蹤滅 【fft + manacher】

BZOJ3160 萬徑人蹤滅 【fft + manacher】

mem getc details namespace ret get fin 運算 main

技術分享圖片
技術分享圖片
技術分享圖片
技術分享圖片

題解

此題略神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; }

BZOJ3160 萬徑人蹤滅 【fft + manacher】