「解题报告」CF356E Xenia and String Problem
比较简单的题。
发现方案很少,考虑对每种方案维护出权值。首先容易得出,好串的长度一定是 \(2^i - 1\) 形式的,这也告诉我们好串的数量上界是 \(O(n \log n)\) 的,那么我们可以对每一个串考虑怎样修改会使得它变成好串。
首先长度为 \(1\) 的一定是好串,直接累计上。
我们分几种情况考虑:
- 这个串的左串与右串均为好串且相等:那么只需要中点的字符出现过 \(1\) 次即可,直接枚举 \(26\) 种情况。如果这个串本来就是好串,那么修改除了这个串以外的任何字符也都是合法的。
- 这个串的左串与右串均为好串且不相等:那么我们必须修改在左串或者是右串才可能使得两者相等。容易发现如果两个串除了中点之外还有不相等的位置,就一定不可能成为好串,因为这些字符一定至少出现两次。那么就只有可能是两个中点不相等了,将其中一个修改成另外一个能够使它变成好串。还要注意整个串的中点出现一次的限制。
- 这个串的左串与右串仅有一个为好串,两串不相等:看看两个串差几个字符,如果仅有一个字符,那么把这个字符改掉即可变成好串,否则一定不可能成为好串。还是要注意中点出现次数。
容易发现只有这三种情况,那么直接对应的去做即可。可以用差分维护答案,复杂度 \(O(n |\Sigma| \log n + n \log^2 n)\)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005, MAXM = MAXN * 26;
int id(int i, int c) { return 26 * (i - 1) + c + 1; }
const __uint128_t B = 131, P = 0xffffffff00000001;
__uint128_t BASE[MAXN], hs[MAXN];
__uint128_t piece(int l, int r) {
return (hs[r] - hs[l - 1] * BASE[r - l + 1] % P + P) % P;
}
int n;
char s[MAXN];
long long ans[MAXM];
int f[MAXN][20];
int lcp(int x, int y, int a, int b) {
int l = 0, r = y - x + 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (piece(x, x + mid - 1) == piece(a, a + mid - 1)) l = mid;
else r = mid - 1;
}
return l;
}
void add(int l, int r, long long v) {
ans[l] += v, ans[r + 1] -= v;
}
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
BASE[0] = 1;
for (int i = 1; i <= n; i++) BASE[i] = BASE[i - 1] * B % P;
for (int i = 1; i <= n; i++) hs[i] = (hs[i - 1] * B + s[i] - 'a' + 1) % P;
memset(f, -1, sizeof f);
for (int i = 1; i <= n; i++) f[i][1] = 1 << (s[i] - 'a');
long long fans = n;
for (int j = 2; j <= 18; j++) {
int len = (1 << j) - 1;
for (int i = 1; i + len - 1 <= n; i++) {
if (f[i][j - 1] != -1 && f[i + len / 2 + 1][j - 1] != -1
&& piece(i, i + len / 2 - 1) == piece(i + len / 2 + 1, i + len - 1)) {
int S = f[i][j - 1] | f[i + len / 2 + 1][j - 1];
if (!(S >> (s[i + len / 2] - 'a') & 1)) {
f[i][j] = S | (1 << (s[i + len / 2] - 'a'));
fans += 1ll * len * len;
}
}
}
}
ans[1] = n;
for (int j = 2; j <= 18; j++) {
int len = (1 << j) - 1;
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1, mid = l + len / 2;
if (f[l][j] != -1) {
add(id(1, 0), id(l - 1, 25), 1ll * len * len);
add(id(r + 1, 0), id(n, 25), 1ll * len * len);
}
if (f[l][j - 1] != -1 && f[mid + 1][j - 1] != -1) {
if (piece(l, mid - 1) == piece(mid + 1, r)) {
int S = f[l][j - 1] | f[mid + 1][j - 1];
for (int k = 0; k < 26; k++) if (!(S >> k & 1)) {
add(id(mid, k), id(mid, k), 1ll * len * len);
}
} else if (lcp(l, mid - 1, mid + 1, r) == len / 4) {
if (j > 2) {
int S = f[l][j - 2] | f[mid + 1][j - 2];
if (S >> (s[mid] - 'a') & 1) continue;
}
int x = s[l + len / 4] - 'a', y = s[mid] - 'a', z = s[r - len / 4] - 'a';
if (x == y) add(id(l + len / 4, z), id(l + len / 4, z), 1ll * len * len);
else if (y == z) add(id(r - len / 4, x), id(r - len / 4, x), 1ll * len * len);
else {
add(id(l + len / 4, z), id(l + len / 4, z), 1ll * len * len);
add(id(r - len / 4, x), id(r - len / 4, x), 1ll * len * len);
}
}
} else if (f[l][j - 1] != -1 || f[mid + 1][j - 1] != -1) {
int S = f[l][j - 1] == -1 ? f[mid + 1][j - 1] : f[l][j - 1];
if (S >> (s[mid] - 'a') & 1) continue;
int x = lcp(l, mid - 1, mid + 1, r) + 1;
if (piece(l + x, mid - 1) == piece(mid + 1 + x, r)) {
if (f[l][j - 1] == -1) {
int y = id(l + x - 1, s[mid + 1 + x - 1] - 'a');
add(y, y, 1ll * len * len);
} else {
int y = id(mid + 1 + x - 1, s[l + x - 1] - 'a');
add(y, y, 1ll * len * len);
}
}
}
}
}
for (int i = id(1, 0); i <= id(n, 25); i++) {
ans[i] += ans[i - 1];
fans = max(fans, ans[i]);
}
printf("%lld\n", fans);
return 0;
}