专栏文章

泪雨 Namid[A]me

P9282题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minik65y
此快照首次捕获于
2025/12/02 02:59
3 个月前
此快照最后确认于
2025/12/02 02:59
3 个月前
查看原文
宝宝泪雨。只需要哈希和 manacher。
发现 ff 值上界在 O(logn)O(\log n),下文直接用 logn\log n 代替 kk
考察一个子串 [l,r][l,r]ff 值,发现直接哈希判断回文就做到 O(logn)O(\log n) 了。
一个对答案有贡献的串首先本身得是回文串,所以考虑 manacher,统计 gi,jg_{i,j} 表示回文中心为 ii 的回文串中 ff 值为 jj 的串的个数,发现我擦这不是我们 P12977 泪雨 Namid[A]me 吗,具体而言,普通 manacher 求出 did_i 表示中心为 ii 的最长回文半径时,dd 的转移中会从当前回文段的对称位置 l+ril+r-i 转移 dimin(ri,dl+ri)d_i \gets \min(r-i,d_{l+r-i}),我们同理用这个维护 gi,jg_{i,j},转移 gi,jgl+ri,jg_{i,j}\gets g_{l+r-i,j},然后 did_i 变长的时候在 gig_i 对应位置加一,但是发现无法去除 dl+rid_{l+r-i}rir-i 段以外的贡献,于是我们暴力收缩到 rir-i 去除贡献即可,能证明复杂度依然是 O(n)O(n),但是要求多个回文中心取到同一个右端点时,回文段左端点取最大的。具体证明可见泪雨题解区。统计完 gg 后,ff 值为 kk 的串个数即为 g,k\sum g_{*,k}。时间复杂度 O(nlogn)O(n\log n)
CPP
#include <bits/stdc++.h>
#define LL long long
#define ull unsigned long long
#define uint unsigned int
using namespace std;
const int N = 2e5 + 10;
const int M = 40;
int n, m, lim = 30;
int res[N][M], D[N]; LL Ans[M];

const ull MOD = 1e9 + 123;
const ull P = 233;
char S[N], T[N]; ull hsh1[N], hsh2[N], pw[N];
ull get1(int l, int r) { 
    return (hsh1[r] + MOD - hsh1[l - 1] * pw[r - l + 1] % MOD) % MOD; 
}
ull get2(int l, int r) { 
    return (hsh2[l] + MOD - hsh2[r + 1] * pw[r - l + 1] % MOD) % MOD; 
}
bool palind(int l, int r) { return get1(l, r) == get2(l, r); }
int _calc(int l, int r) {
	int cnt = 0; for (; l < r && palind(l, r); ++ cnt, r = (l + r) / 2);
	return cnt + (l == r);
}
int calc(int l, int r) { return (T[l] == '#' ? 0 : _calc(l / 2, r / 2)); }

int main() {
	freopen(".in", "r", stdin); freopen(".out", "w", stdout);
	ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
	cin >> n >> m >> (S + 1); pw[0] = 1;
	for (int i = 1; i <= n; i ++) pw[i] = pw[i - 1] * P % MOD;
	for (int i = 1; i <= n; i ++) hsh1[i] = (hsh1[i - 1] * P + S[i]) % MOD;
	for (int i = n; i >= 1; i --) hsh2[i] = (hsh2[i + 1] * P + S[i]) % MOD;
	T[1] = '#'; for (int i = 1; i <= n; i ++) T[i * 2] = S[i], T[i * 2 + 1] = '#';
	int tot = n * 2 + 1;
	for (int i = 1, l = 1, r = 0; i <= tot; i ++) {
		if (i <= r) {
			int p = l + r - i; D[i] = min(D[p], r - i);
			while (i - D[i] - 1 >= 1 && i + D[i] + 1 <= tot 
                   && T[i - D[i] - 1] == T[i + D[i] + 1])
				++ D[i];
			for (int j = 0; j <= lim; j ++) res[i][j] = res[l + r - i][j];
			for (int j = D[p]; j > r - i; j --) res[i][calc(p - j, p + j)] --;
			for (int j = r - i + 1; j <= D[i]; j ++) res[i][calc(i - j, i + j)] ++;
		} else {
			res[i][1] = (T[i] != '#');
			while (i - D[i] - 1 >= 1 && i + D[i] + 1 <= tot 
					&& T[i - D[i] - 1] == T[i + D[i] + 1])
				++ D[i], res[i][calc(i - D[i], i + D[i])] ++;
		}
		if (i + D[i] >= r) r = i + D[i], l = i - D[i];
		for (int j = 0; j <= lim; j ++) Ans[j] += res[i][j];
	}
	for (int i = 1; i <= m; i ++) cout << Ans[i] << " \n"[i == m];
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...