题解:CF1913F Palindromic Problem

题意

给定长度为 \(n\) 的小写字母串 \(s\)。你可以将至多一个位置修改为任意小写字母,使得新串的回文子串数量最多,在此基础上,最小化新串的字典序。\(1\leq |s|\leq 3\times 10^5\)

题解

居然没有调试一遍过了,有点帅。

比较无聊的题。

先对 \(s\) 跑一次 Manacher 求出回文半径,全部相加可以得到回文子串数量。

考虑预处理出 \(f_{i,j}\) 表示将 \(s_i\) 修改为 \(j\) 的增量。枚举回文中心(可以是字符或字符间的间隔),我们知道一个中心对答案的贡献是其回文半径,因此考察修改某个位置对该中心的回文半径的影响。不妨假设回文中心是 \(s_i\),回文半径为 \(d_i\),可以发现,只有把 \(j=i-d_i\lor j=i+d_i\lor j\in[i-d_i+1,i)\lor j\in(i,i+d_i-1]\)\(s_j\) 修改为和原来不同的字符回文半径才会改变。分类讨论:

  • \(j=i-d_i\lor j=i+d_i\):必须要令 \(s_{i-d_i}\gets s_{i+d_i}\) 或令 \(s_{i+d_i}\gets s_{i-d_i}\) 才会使得 \(d_i\) 改变(当然也要保证 \(s_{i-d_i}\neq s_{i+d_i}\))。设 \(|\operatorname{lcp}(s_{i+d_i+1,n},\operatorname{rev}(s_{1,i-d_i-1}))|=t\),则此时对答案的增量为 \(t+1\)。对 \(s+\texttt{#}+\operatorname{rev}(s)\) 做一遍 SA,用 ST 表维护 \(ht\) 即可 \(\mathcal{O}(n\log{n})-\mathcal{O}(1)\) 维护。
  • \(j\in[i-d_i+1,i)\lor j\in(i,i+d_i-1]\):回文半径显然减小。若 \(j\in[i-d_i+1,i)\) 则对答案贡献 \(-(j-i+d_i)\),否则若 \(j\in(i,i+d_i-1]\) 则对答案负贡献 \(-(i+d_i-j)\)。显然是区间加等差数列的形式,二阶差分简单维护。

回文中心为字符间隔的 case 基本一样,这里不再赘述。

预处理出 \(f_{i,j}\) 后就很简单了。比较两个二元组时,显然优先选 \(f\) 值最大的,若 \(f\) 值相同做一些简单讨论使得新串的字典序最小即可。时间复杂度 \(\mathcal{O}(n\log{n})\)

代码
#include <bits/stdc++.h>

using namespace std;

#define lowbit(x) ((x) & -(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int N = 6e5 + 5, LGN = 20, C = 26;
const ll INF = 1e18;

template<typename T> inline void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> inline void chk_max(T &x, T y) { x = max(x, y); }

int n, d0[N], d1[N];
ll sum, f[N][C], c[N];
pii ans;
string s, revs;

struct ST {
	int mn[LGN][N];
	void build(int n) {
		for (int i = 1; i <= (31 ^ __builtin_clz(n)); ++i) for (int j = 1; j <= n - (1 << i) + 1; ++j)
			mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + (1 << i - 1)]);
	}
	int query(int l, int r) {
		int k = 31 ^ __builtin_clz(r - l + 1);
		return min(mn[k][l], mn[k][r - (1 << k) + 1]);
	}
} st;
struct SA {
	int n, buc[N], rk[N], trk[N << 1], id[N], rid[N], sa[N], ht[N];
	void build(const string &s) {
		n = s.size() - 1;
		int m = 'z';
		for (int i = 1; i <= n; ++i) ++buc[rk[i] = s[i]];
		for (int i = 1; i <= m; ++i) buc[i] += buc[i - 1];
		for (int i = n; i; --i) sa[buc[rk[i]]--] = i;
		for (int w = 1, p = 0; p < n; w <<= 1, m = p) {
			p = 0;
			for (int i = n - w + 1; i <= n; ++i) id[++p] = i;
			for (int i = 1; i <= n; ++i) if (sa[i] > w) id[++p] = sa[i] - w;
			fill(buc + 1, buc + m + 1, 0);
			for (int i = 1; i <= n; ++i) ++buc[rid[i] = rk[id[i]]];
			for (int i = 1; i <= m; ++i) buc[i] += buc[i - 1];
			for (int i = n; i; --i) sa[buc[rid[i]]--] = id[i];
			copy(rk + 1, rk + n + 1, trk + 1), p = 0;
			for (int i = 1; i <= n; ++i) rk[sa[i]] = p += trk[sa[i - 1]] != trk[sa[i]] || trk[sa[i - 1] + w] != trk[sa[i] + w];
		}
		for (int i = 1, j = 0; i <= n; ++i) {
			if (rk[i] == 1) continue;
			if (j) -- j;
			while (j <= n - max(i, sa[rk[i] - 1]) && s[i + j] == s[sa[rk[i] - 1] + j]) ++j;
			ht[rk[i]] = j;
		}
		for (int i = 1; i <= n; ++i) st.mn[0][i] = ht[i];
		st.build(n);
	}
	int lcp(int x, int y) { return st.query(min(rk[x], rk[y]) + 1, max(rk[x], rk[y])); }
} sa;

void manacher() {
	int l = 0, r = 0;
	for (int i = 1; i <= n; ++i) {
		d1[i] = i <= r ? min(d1[l + r - i], r - i + 1) : 0;
		while (i > d1[i] && i + d1[i] <= n && s[i - d1[i]] == s[i + d1[i]]) ++d1[i];
		sum += d1[i];
		if (i + d1[i] - 1 > r) l = i - d1[i] + 1, r = i + d1[i] - 1;
	}
	l = r = 0;
	for (int i = 1; i <= n; ++i) {
		d0[i] = i <= r ? min(d0[l + r - 1 - i], r - i) : 0;
		while (i > d0[i] && i + d0[i] < n && s[i - d0[i]] == s[i + d0[i] + 1]) ++d0[i];
		sum += d0[i];
		if (i + d0[i] > r) l = i - d0[i] + 1, r = i + d0[i];
	}
}
void add(int l, int r, int s, int d) {
	int t = s + (r - l) * d;
	c[l] += s, c[l + 1] += d - s, c[r + 1] -= d + t, c[r + 2] += t;
}
bool cmp(const pii &a, const pii &b) {
	auto [x, c1] = a; auto [y, c2] = b;
	if (f[x][c1] ^ f[y][c2]) return f[x][c1] > f[y][c2];
	if (x == y) return c1 < c2;
	bool flag = x > y;
	if (flag) swap(x, y), swap(c1, c2);
	if (c1 ^ s[x]) return flag ^ (c1 < s[x]);
	return flag ^ (c2 > s[y]);
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n >> s, revs = s, s = '#' + s;
	reverse(revs.begin(), revs.end());
	sa.build(s + '#' + revs), manacher();
	for (int i = 1; i <= n; ++i) s[i] -= 'a';
	for (int i = 1; i <= n; ++i) {
		int l = i - d1[i] + 1, r = i + d1[i] - 1;
		if (l > 1 && r < n && s[l - 1] != s[r + 1]) {
			int d = 1 + (l - 2 >= 1 && r + 2 <= n ? sa.lcp(r + 2, sa.n - (l - 2) + 1) : 0);
			f[l - 1][s[r + 1]] += d, f[r + 1][s[l - 1]] += d;
		}
		if (d1[i] > 1) add(l, i - 1, -1, -1), add(i + 1, r, l - i, 1);
		if (i == n) continue;
		l = i - d0[i] + 1, r = i + d0[i];
		if (l > 1 && r < n && s[l - 1] != s[r + 1]) {
			int d = 1 + (l - 2 >= 1 && r + 2 <= n ? sa.lcp(r + 2, sa.n - (l - 2) + 1) : 0);
			f[l - 1][s[r + 1]] += d, f[r + 1][s[l - 1]] += d;
		}
		if (d0[i]) add(l, i, -1, -1), add(i + 1, r, l - i - 1, 1);
	}
	for (int i = 1; i <= n; ++i) c[i] += c[i - 1];
	for (int i = 1; i <= n; ++i) c[i] += c[i - 1];
	for (int i = 1; i <= n; ++i) for (int j = 0; j < C; ++j) if (s[i] ^ j) f[i][j] += c[i];
	f[0][0] = -INF;
	for (int i = 1; i <= n; ++i) for (int j = 0; j < C; ++j) ans = min(ans, {i, j}, cmp);
	auto [x, y] = ans;
	cout << sum + f[x][y] << '\n';
	s[x] = y;
	for (int i = 1; i <= n; ++i) cout << (char)(s[i] + 'a');
	return 0;
}
posted @ 2025-11-21 10:34  P2441M  阅读(7)  评论(0)    收藏  举报