CF356E – Xenia and String Problem – 题解

CodeForces CF356E – Xenia and String Problem

这是一种不需要任何数据结构和处理字符串的工具,但细节繁多的做法。

设全串为 s1ns_{1\dots n}。不难发现,Gray string 的串长必然是 2w1(w1)2^w-1\quad (w\geq 1) 的形式。若其长为 2w12^w-1,我们称其为 w1w-1 阶 Gray string。

同时用归纳法可以证明,设一个 ww 阶 Gray string 的起始位置为 x+1x+1(其终到位置为 x+2w+1x+2^{w+1}),那么必然有 k{0,1,,w},sx+2k=sx+2k+2k+1=sx+2k+2×2k+1==sx+2k+(2wk1)2k+1i,j,0i<jw,sx+2isx+2j \forall k\in\{0,1,\cdots,w\},s_{x+2^k}=s_{x+2^k+2^{k+1}}=s_{x+2^k+2\times 2^{k+1}}=\cdots=s_{x+2^k+(2^{w-k}-1)2^{k+1}}\\ \forall i,j,0\leq i<j\leq w,s_{x+2^i}\neq s_{x+2^j} 成立。例如一个 33 阶 Gray string 可以被描述为 abacabadabacaba\mathtt{abacabadabacaba}

考虑在不更改字符的情况下,计算所有位置 x(0x<n)x\quad (0\leq x<n) 有多少 ww 满足 sx+1x+2w+1s_{x+1\dots x+2^{w+1}} 是 Gray string。我们记 g(y,w)g(y,w) 为最大的 kk,满足 sy=sy+2w+1=sy+2×2w+1==sy+(k1)2w+1s_y=s_{y+2^{w+1}}=s_{y+2\times 2^{w+1}}=\cdots=s_{y+(k-1)2^{w+1}},也即间隔为 2w+12^{w+1}极长邻项字符相等的链。这可以暴力 O(nlogn)\newcommand\bigO{\operatorname{O}}\bigO(n\log n) 求出。那么位置 xx 能够贡献一个 ww 阶 Gray string,当且仅当 0iw,wi+log2g(x+2i,i)0i<jw,sx+2isx+2j \forall 0\leq i\leq w,w\leq i+\lfloor\log_2 g(x+2^i,i)\rfloor\\ \forall 0\leq i<j\leq w,s_{x+2^i}\neq s_{x+2^j} 这依然可以 O(nlogn)\bigO(n\log n) 求出——我们从小到大遍历 ww 并更新上文式子的最小值,直到 ww 超过这个最小值或者出现重复字符。固定位置 xx,记 w0w_0 是满足上式的最大 ww

现在思考当某个位置的字符被替换后,xx 的贡献有何变化。我们分两个部分考虑:删除新增。记在全串范围内,将 sus_u 变为字符 cc 会新增 h(u,c)h(u,c) 的权值和。

被删除的串

易得对于所有 u(x<u<x+2w+1)u\quad (x<u<x+2^{w+1})sus_u 的改变将直接破坏 Gray string sx+1x+2w+11s_{x+1\dots x+2^{w+1}-1};特别地,若 u=x+2wu=x+2^w(该 Gray string 的中央字符),且有 0i<w,susx+2i\forall 0\leq i<w, {s_u}’\neq s_{x+2^i}(不与更低阶的字符重复),则这个 ww 阶 Gray string 不会被破坏。

于是我们设立差分数组 d(x)(1xn)d(x)\quad(1\leq x\leq n),维护破坏造成的负权值。记 bw=(2w+11)2b_w=(2^{w+1}-1)^2。令 d(x+1)d(x+1)bwd(x+2w+1)d(x+2w+1)+bwd(x+2w)d(x+2w)+bwd(x+2w+1)d(x+2w+1)bw(中央字符可以改变)i,0i<w,h(x+2w,sx+2i)h(x+2w,sx+2i)(2w+11)2(但不能改为重复字符)\begin{aligned} {d(x+1)}’&\leftarrow d(x+1)-b_w\\ {d(x+2^{w+1})}’&\leftarrow d(x+2^{w+1})+b_w\\ {d(x+2^w)}’&\leftarrow d(x+2^w)+b_w\\ {d(x+2^w+1)}’&\leftarrow d(x+2^w+1)-b_w&\text{(中央字符可以改变)}\\ \forall i,0\leq i<w,{h(x+2^w,s_{x+2^i})}’&\leftarrow h(x+2^w,s_{x+2^i})-(2^{w+1}-1)^2&(但不能改为重复字符) \end{aligned}

新增的串

我们称位置 xx 的扩展被阻拦当且仅当有 0ww0,w0<w+log2g(x+2w,w)\forall 0\leq w\leq w_0,w_0<w+\lfloor\log_2 g(x+2^w,w)\rfloor。这也就意味着 wo+1w_o+1 阶位置出现了重复字符;反之,未被阻拦是说 w0w_0 恰等于了限制式的上界。按照这个定义,同时出现 w0w_0 卡到上界和 w0+1w_0+1 阶位置有重复字符的话,会被归为“未阻拦”。

未被阻拦

我们令 jj 是所有 w(0ww0)w\quad (0\leq w\leq w_0) 中取到 w0=w+log2g(x+2w,w)w_0=w+\lfloor\log_2 g(x+2^w,w)\rfloor 的值,也即上界的来源。若 jj 不唯一,由于替换单个字符最多更改一个 g(x+2w,w)g(x+2^w,w),故而 w0w_0 不会改变,不可能在 xx 处有新的贡献。

jj 唯一,有两种可能使得 g(x+2j,j)g(x+2^j,j) 变大。记应用 g(x+2j,j){g(x+2^j,j)}’ 得到的新上界为 w0{w_0}’

  • u=x+2j+2j+1g(x+2j,j)u=x+2^j+2^{j+1}g(x+2^j,j)su=sx+2j{s_u}’=s_{x+2^j},也即 uujj 阶链尾的下一个位置。将 sus_u 设置为 sx+2js_{x+2^j},就是将它合并到 jj 阶极长链上——这甚至还可能导致 uu 两侧两条 jj 阶链的合并;
  • j=w0j=w_0u2=u=x+2ju_2=u=x+2^j0ww0,susx+2w\forall 0\leq w\leq w_0, {s_u}’\neq s_{x+2^w}。由 j=w0j=w_0 可以推出 log2g(x+2j,j)=0    g(x+2j,j)=1\lfloor\log_2 g(x+2^j,j)\rfloor=0\iff g(x+2^j,j)=1,也即这条链长为 11。此时将该位置本身替换成 su+2j+1s_{u+2^{j+1}}(如果其与低阶的链不重复),可能会引发两条链的合并,较原串更优。

不难说明,其他任何位置替换任何字符,都只会使得 xx 的贡献削减(g(x+2w,w)g(x+2^w,w) 不增);而负贡献已经在上文中计算完成了。如果上文两种情况有新的贡献,则我们分别累加 h(u,su)h(u,su)+w=w0+1w0bwh(u,{s_u}’)’\leftarrow h(u,{s_u}’)+\sum_{w=w_0+1}^{{w_0}’} b_w

被阻拦

可以验证,被阻拦时只有改变 su=x+2w0+1s_{u=x+2^{w_0+1}},也即 w0+1w_0+1 阶链的首字符,才有可能在 xx 处有新贡献。若有 susx+2w0+1x+2w0+2{s_u}’\neq s_{x+2^{w_0+1}x+2^{w_0+2}}(试图合并两条 w0+1w_0+1 阶链),可能有 w0>w0+1{w_0}’>w_0+1;否则必然有 g(u,w0+1)=1,w0=w0+1{g(u,w_0+1)}’=1,{w_0}’=w_0+1。只有一种情况需要重新暴力计算。及时更新 h(u,)h(u,*) 即可。

最后我们从前到后累加差分,利用 hh 更新答案即可。

按照上述方法精细实现,可以做到 O(nlogn)\bigO(n\log n),常数小。是为目前本题最优解

Submission #187976651

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
#include <bits/stdc++.h> using namespace std; #define inl __attribute__((always_inline)) inline typedef long long ll; typedef pair <int, int> pint; #define fst first #define scd second #define all(p) begin (p), end (p) #define empb emplace_back constexpr int N = 1e5 + 10; constexpr ll b[] = { 1, 9, 49, 225, 961, 3969, 16129, 65025, 261121, 1046529, 4190209, 16769025, 67092481, 268402689, 1073676289, 4294836225, 17179607041 }; constexpr ll bpre[] = { 1, 10, 59, 284, 1245, 5214, 21343, 86368, 347489, 1394018, 5584227, 22353252, 89445733, 357848422, 1431524711, 5726360936, 22905967977 }; int n, g[17][N]; char s[N]; ll ans, dt[N], ata[N][26]; inl int cnxt (int x, int i) { return x + g[i][x+(1<<i)] * (1<<i+1) + (1<<i); } inl int ced (int x, int i) { return x + g[i][x+(1<<i)] * (1<<i+1) - (1<<i); } inl int cst (int x, int i) { return x + (1<<i); } inl int imple (int x) { static bool occ[26]; int w = 0, lmt = INT_MAX, y; memset (occ, 0, 26); while (w <= lmt && !occ[s[y = cst (x, w)]-'a']) lmt = min (lmt, __lg (g[w][y]) + w), occ[s[y] - 'a'] = 1, ++w; return --w; } int main () { /* */ scanf ("%s", s + 1); n = strlen (s + 1); for (int w = 0; (1<<w) <= n; ++w) for (int st = 1; st <= 1<<w+1; ++st) { int x = st, y = st, cnt = 0; while (x <= n) { while (y <= n && s[x] == s[y]) y += 1<<w+1, ++cnt; while (x < y) g[w][x] = cnt--, x += 1<<w+1; } } for (int x = 0; x < n; ++x) { static bool occ[26]; static int bd[17], lst[17]; memset (occ, 0, 26); int w = 0, lmt = INT_MAX, y, z, c, _g, alp; while (w <= lmt && !occ[s[y = cst (x, w)]-'a']) lmt = min (lmt, bd[w] = __lg (g[w][y]) + w), occ[lst[w++] = s[y] - 'a'] = 1; ans += bpre[--w], dt[x + 1] -= bpre[w]; for (int i = 0; i <= w; ++i) { dt[x+(1<<i+1)] += b[i], dt[y = cst (x, i)] += b[i], dt[y+1] -= b[i]; for (int k = 0; k < i; ++k) ata[y][lst[k]] -= b[i]; } if (w == lmt) { // ? 无阻拦/非严格阻拦。 int j = -1; for (int i = 0; i <= w; ++i) if (bd[i] == w) j = ~j ? INT_MAX : i; if (j == INT_MAX) continue; // ? 在j阶后面补足。 if ((z = cnxt (x, j)) > n) goto KAWA; c = s[z], s[z] = s[y = cst (x, j)]; _g = g[j][y], g[j][y] += 1 + ((alp = z + (1<<j+1)) <= n && s[alp] == s[y] ? g[j][alp] : 0); ata[z][s[y]-'a'] += bpre[imple (x)] - bpre[w]; g[j][y] = _g, s[z] = c; // ? 若有j = w,则自然有j阶连通块大小为1。尝试改换它自己的颜色。 KAWA: if (j != w || (z = cnxt (x, w)) > n || occ[s[z] - 'a']) continue; c = s[y = cst (x, w)], s[y] = s[z]; _g = g[w][y], g[w][y] += g[w][z]; ata[y][s[z]-'a'] += bpre[imple (x)] - bpre[w]; g[w][y] = _g, s[y] = c; } else { // ? 严格阻拦。 // ? 添置一个没有出现过的字符。 for (c = 0, y = cst (x, w+1); c < 26; ++c) ata[y][c] += !occ[c] * b[w+1]; if (g[w+1][y] > 1 || (z = y + (1<<w+2)) > n) continue; ata[y][s[z]-'a'] -= b[w+1]; c = s[y], s[y] = s[z], g[w+1][y] += g[w+1][z]; ata[y][s[z]-'a'] += bpre[imple (x)] - bpre[w]; s[y] = c, g[w+1][y] = 1; } } for (ll ima = ans, x = 1; x <= n; ++x) ans = max (ans, (ima += dt[x]) + *max_element (all (ata[x]))); printf ("%lld", ans); return 0; }
  • 2023年1月5日