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 }

3160_Gromah

时间: 2024-10-29 19:05:28

BZOJ 3160 万径人踪灭 解题报告的相关文章

bzoj 3160: 万径人踪灭 manachar + FFT

3160: 万径人踪灭 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 133  Solved: 80[Submit][Status][Discuss] Description Input Output Sample Input Sample Output HINT 以每一个位置为中心,分别处理连续一块的回文串,回文序列个数. 比较容易看出是FFT+manachar,但是FFT还是不太熟悉,特别要注意三层for语句中i,j,k不能写错,这东西很难调

BZOJ 3953 Self-Assembly 解题报告

首先,我们可以先考虑一个暴力一点的算法: 对于任意两个分子,如果它们能以至少一种进行匹配,那么我们就在这两个分子之间连一条边. 然后如果我们能找到一个环,就说明是 unbounded,否则就是 bounded. 复杂度是 $O(n^2)$ 的,然而 $n \le 40000$ ,显然是不行的. 考虑优化.我们注意到本质不同的边有 $26$ 种,那么我们应该能省去很多不必要的边. 令 $inv(x)$ 为与类型为 $x$ 的离子匹配的离子,如 $inv(A+)=A-$. 对于在同一个分子上的两个离

[BZOJ 3160] 万径人踪灭

题意 给定一个长度为 n , 由 'a', 'b' 组成的字符串 S . 问有多少个子序列, 满足: ① 坐标对称. ② 字符对称. ③ 不连续. n <= 100000 . 分析 坐标对称, 则要满足对应坐标的值相同. 满足①② 的所有用 FFT 求. 满足①②!③ 的所有用 Manacher 求. 实现 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cc

BZOJ 3901 棋盘游戏 解题报告

这题有个重要性质: 我们设 Flag[i][j] 表示 (i, j) 是否被奇数个操作所覆盖, 也就是操作次数对 2 取模. 设 x = (n + 1) / 2. 那么对于所有的合法的操作方案, 令 1 <= i <= x , 1 <= j < x, 都有 Flag[i][j] ^ Flag[i][x] ^ Flag[i][j + x] = 0 令 1 <= i < x , 1 <= j <= x, 都有 Flag[i][j] ^ Flag[x][j] ^

BZOJ 4302 Buildings 解题报告

这个题好像很有趣的样子. 题目分析: 房间都是 $1\times k$ 的,也就是一条一条的.这个好像比较显然的样子. 一个房间如果要覆盖某个格子$u$,那么这个房间的面积至少为 $dis(u, Boundry)$,即其到边界的距离,这个好像也比较显然的样子. 于是答案至少是 $max\{dis(u, Boundry)\}$,然后可以通过构造来取到最小值,即答案就是$max\{dis(u, Boundry)\}$. 算法流程: 特判:如果输入的是一个边长为一个奇数的正方形,且 $(x,y)$ 恰

BZOJ 4127 Abs 解题报告

这个题感觉很厉害的样子.. 首先我们注意到一点:每次加的 $d$ 都是非负的. 那么就说明一个数只可能从负数变成非负数并且只会变一次. 所以我们就可以暴力地去改变一个数的正负情况. 然后我们就可以用树链剖分,维护一下区间的最大负数和负数的个数就可以了. 时间复杂度 $O(n\log^2 n)$,空间复杂度 $O(n)$. 1 #include <cstdio> 2 typedef long long LL; 3 #define N 262144 + 5 4 #define INF 123456

BZOJ 3160 万径人踪灭 Manacher算法+快速傅里叶变换

题目大意:给定一个由'a'和'b'构成的字符串,求不连续回文子序列的个数 首先回文一定是将字符串倍增 由于求的是不连续回文子序列的个数 因此我们可以求出总回文子序列的个数,然后减掉连续的 连续的就是回文子串 用Manacher算法可以O(n)求解 不连续的就有些难搞了 首先我们令f[i]表示以i为中心的对称字符对个数 比如s[]=$#a#b#a 那么s[4]='b' f[4]=2 那么对于每个中心i我们有(2^f[i])-1种方案 答案即Σ[1<=i<=n*2+1]((2^f[i])-1) 问

BZOJ 3160 万径人踪灭 FFT+Manacher

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

BZOJ 1051 最受欢迎的牛 解题报告

题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[Submit][Status][Discuss] Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎.你的任务是求出有多少头 牛被所有的牛