BZOJ 3160 万径人踪灭 解题报告
这个题感觉很神呀。将 FFT 和 Manacher 有机结合在了一起。
首先我们不管那个 “不能连续” 的条件,那么我们就可以求出有多少对字母关于某一条直线对称,然后记 $T_i$ 为关于直线 $i$ 对称的字母对的数量,那么答案(暂记为 $Ans$)就会是:
$$Ans = \sum 2^{T_i}-1$$
在不管那个 “不能连续” 的条件的时候,这个应该是显然的。
怎么算的话,我们弄两次。分别把 $a$ 和 $b$ 当做 $1$,另一个当做 $0$,然后就可以得到一个多项式,将这个多项式平方一下就可以得到所有的 $T_i$ 了,具体用 FFT 实现。
那么我们来管一管这个条件。
我们就可以用 Manacher 求出每一条直线的最长回文半径,然后记 $R_i$ 为直线 $i$ 的最长回文半径,那么实际上的总答案就会是:
$$Ans - \sum R_i$$
然后就做完啦。令 $n$ 为字符串的长度:
时间复杂度 $O(n\log n)$,空间复杂度 $O(n)$。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long LL; 6 #define N 262144 + 5 7 #define _Mod 1000000007 8 #define Mod 998244353 9 #define g 3 10 11 int n, len, Inv_len, d, ans, e[2][N], Rev[N], A[N], T[N], R[N]; 12 char s[N]; 13 14 inline int Inc(int u, int v, int p) 15 { 16 return u + v - (u + v >= p ? p : 0); 17 } 18 19 inline int power(int u, int v, int p) 20 { 21 int res = 1; 22 for (; v; v >>= 1) 23 { 24 if (v & 1) res = (LL) res * u % p; 25 u = (LL) u * u % p; 26 } 27 return res; 28 } 29 30 inline void FFT_Prepare() 31 { 32 for (len = n << 1; len != (len & -len); len += (len & -len)) ; 33 for (int i = len; i > 1; i >>= 1) d ++; 34 int w = power(g, (Mod - 1) / len, Mod); 35 int Inv_w = power(w, Mod - 2, Mod); 36 Inv_len = power(len, Mod - 2, Mod); 37 for (int i = 0; i < len; i ++) 38 { 39 e[0][i] = !i ? 1 : (LL) e[0][i - 1] * w % Mod; 40 e[1][i] = !i ? 1 : (LL) e[1][i - 1] * Inv_w % Mod; 41 for (int j = 0; j < d; j ++) 42 if ((i >> j) & 1) Rev[i] += 1 << (d - j - 1); 43 } 44 } 45 46 inline void FFT(int *p, int op) 47 { 48 for (int i = 0; i < len; i ++) 49 if (Rev[i] > i) swap(p[Rev[i]], p[i]); 50 for (int k = 1, s = 1; k < len; k <<= 1, s ++) 51 for (int i = 0; i < len; i ++) 52 { 53 if (i & k) continue ; 54 int t = (i & (k - 1)) << (d - s); 55 int u = Inc(p[i], (LL) p[i + k] * e[op][t] % Mod, Mod); 56 int v = Inc(p[i], (LL) (Mod - p[i + k]) * e[op][t] % Mod, Mod); 57 p[i] = u, p[i + k] = v; 58 } 59 } 60 61 inline void FFT_Work(char key) 62 { 63 memset(A, 0, sizeof(A)); 64 for (int i = 0; i < n; i ++) 65 A[i] = (s[i] == key); 66 FFT(A, 0); 67 for (int i = 0; i < len; i ++) 68 A[i] = (LL) A[i] * A[i] % Mod; 69 FFT(A, 1); 70 for (int i = 0; i < len; i ++) 71 T[i] = Inc(T[i], (LL) A[i] * Inv_len % Mod, Mod); 72 } 73 74 inline void Manacher() 75 { 76 for (int i = (n << 1); i >= 0; i --) 77 s[i] = i & 1 ? s[i >> 1] : 'c'; 78 int mx = -1, id; 79 for (int i = 0; i <= (n << 1); i ++) 80 { 81 if (mx > i) 82 R[i] = min(R[id * 2 - i], mx - i); 83 else R[i] = 1; 84 for (; i + R[i] <= (n << 1) && i - R[i] >= 0 && s[i + R[i]] == s[i - R[i]]; R[i] ++) ; 85 if (i + R[i] > mx) 86 mx = i + R[i], id = i; 87 } 88 } 89 90 int main() 91 { 92 scanf("%s", s); 93 n = strlen(s); 94 FFT_Prepare(); 95 for (char ch = 'a'; ch <= 'b'; ch ++) 96 FFT_Work(ch); 97 for (int i = 0; i < len; i ++) 98 { 99 T[i] = (T[i] + 1) >> 1; 100 ans = Inc(ans, power(2, T[i], _Mod) - 1, _Mod); 101 } 102 Manacher(); 103 for (int i = 0; i <= (n << 1); i ++) 104 ans = Inc(ans, _Mod - R[i] / 2, _Mod); 105 printf("%d\n", ans); 106 107 return 0; 108 }