BZOJ 3160 万径人踪灭 解题报告
2015-07-16 20:10
344 查看
这个题感觉很神呀。将 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)$。
3160_Gromah
首先我们不管那个 “不能连续” 的条件,那么我们就可以求出有多少对字母关于某一条直线对称,然后记 $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)$。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; #define N 262144 + 5 #define _Mod 1000000007 #define Mod 998244353 #define g 3 int n, len, Inv_len, d, ans, e[2] , Rev , A , T , R ; char s ; inline int Inc(int u, int v, int p) { return u + v - (u + v >= p ? p : 0); } inline int power(int u, int v, int p) { int res = 1; for (; v; v >>= 1) { if (v & 1) res = (LL) res * u % p; u = (LL) u * u % p; } return res; } inline void FFT_Prepare() { for (len = n << 1; len != (len & -len); len += (len & -len)) ; for (int i = len; i > 1; i >>= 1) d ++; int w = power(g, (Mod - 1) / len, Mod); int Inv_w = power(w, Mod - 2, Mod); Inv_len = power(len, Mod - 2, Mod); for (int i = 0; i < len; i ++) { e[0][i] = !i ? 1 : (LL) e[0][i - 1] * w % Mod; e[1][i] = !i ? 1 : (LL) e[1][i - 1] * Inv_w % Mod; for (int j = 0; j < d; j ++) if ((i >> j) & 1) Rev[i] += 1 << (d - j - 1); } } inline void FFT(int *p, int op) { for (int i = 0; i < len; i ++) if (Rev[i] > i) swap(p[Rev[i]], p[i]); for (int k = 1, s = 1; k < len; k <<= 1, s ++) for (int i = 0; i < len; i ++) { if (i & k) continue ; int t = (i & (k - 1)) << (d - s); int u = Inc(p[i], (LL) p[i + k] * e[op][t] % Mod, Mod); int v = Inc(p[i], (LL) (Mod - p[i + k]) * e[op][t] % Mod, Mod); p[i] = u, p[i + k] = v; } } inline void FFT_Work(char key) { memset(A, 0, sizeof(A)); for (int i = 0; i < n; i ++) A[i] = (s[i] == key); FFT(A, 0); for (int i = 0; i < len; i ++) A[i] = (LL) A[i] * A[i] % Mod; FFT(A, 1); for (int i = 0; i < len; i ++) T[i] = Inc(T[i], (LL) A[i] * Inv_len % Mod, Mod); } inline void Manacher() { for (int i = (n << 1); i >= 0; i --) s[i] = i & 1 ? s[i >> 1] : 'c'; int mx = -1, id; for (int i = 0; i <= (n << 1); i ++) { if (mx > i) R[i] = min(R[id * 2 - i], mx - i); else R[i] = 1; for (; i + R[i] <= (n << 1) && i - R[i] >= 0 && s[i + R[i]] == s[i - R[i]]; R[i] ++) ; if (i + R[i] > mx) mx = i + R[i], id = i; } } int main() { scanf("%s", s); n = strlen(s); FFT_Prepare(); for (char ch = 'a'; ch <= 'b'; ch ++) FFT_Work(ch); for (int i = 0; i < len; i ++) { T[i] = (T[i] + 1) >> 1; ans = Inc(ans, power(2, T[i], _Mod) - 1, _Mod); } Manacher(); for (int i = 0; i <= (n << 1); i ++) ans = Inc(ans, _Mod - R[i] / 2, _Mod); printf("%d\n", ans); return 0; }
3160_Gromah
相关文章推荐
- 习题1-5 打折(discount)
- 【转载】HBase基本概念和hbase shell常用命令用法
- Mobile Services 提交批量数据
- 微信支付:模板消息实现过程备忘
- 开发常用到的terminal命令
- 习题1-4 正弦和余弦(sin和cos)
- 在Unity中使用Lua脚本:语言层和游戏逻辑粘合层处理
- 爱因斯坦1921年在获得诺贝尔奖时的演讲:《我的信仰》
- 【优先队列】POJ3614-Sunscreen
- 年龄排序
- Unity3d-Camera
- OpenGl_ES 使用流程
- iOS学习笔记--(c基础2)
- 看历史评价的尺度
- 自恢复保险丝
- 2015 HUAS Provincial Select Contest #3 A
- 一种用于高清下变换的基函数
- 习题1-3 连续和(sum)
- mysql Innodb缓存命中率
- php的基础架构MVC