题解:P5115 Check,Check,Check one two!

· · 题解

摘自本文。

P5115 Check,Check,Check one two!

f_x=\sum_{l=1}^{k_1} \sum_{r=1}^{k_2}[l+r-1=x]\times l\times r,则我们要求的式子可以转化成 \sum_{i=1}^n \sum_{j=i+1}^n [s_{i-1}\ne s_{j-1}]\times f_{lcp(rk_i,rk_j)},转而枚举 lcp,即 \sum_{i=1}^n f_i\times \sum_{i=1}^n \sum_{j=i+1}^n [lcp(rk_i,rk_j)=k]\times [s_{i-1}\ne s_{j-1}],也就是 \sum_{i=1}^n f_i\times \sum_{i=1}^n \sum_{j=i+1}^n[lcp(i,j)=k]\times [s_{sa_i-1}\ne s_{sa_j-1}]

那么可以考虑枚举每个 lcp 计算其对答案的贡献。

我们仍然可以对 height 数组建出笛卡尔树,对于当前点 x,从它的左右区间所涵盖的后缀范围内各任选一个后缀,则他们的 lcp 为 h_x。但是我们从左右子树选出节点 i,j 后,还需满足 s_{sa_i-1}\ne s_{sa_j-1},显然某个 s_k 只有 26 种取值,于是可以设 g_{i,j} 表示在节点 i 的子树中满足 sa_{sa_y-1}=jy 的个数。转移有 g_{x,i}=g_{lson,i}+g_{rson,i},对答案的贡献为 \sum_{i\ne j} g_{lson,i}\times g_{rson,j},这个是好算的。

考虑计算 f_x

```cpp #include <bits/stdc++.h> using namespace std; #define int long long #define ull unsigned long long const int N = 100005; int a[N], b[N], sa[N], h[N], rk[N], t[N], q[N]; string s; int n, m; void getsa() { memset(t, 0, sizeof(t)); memset(sa, 0, sizeof(sa)); for (int i = 1; i <= n; i++) { a[i] = s[i]; ++t[a[i]]; } for (int i = 2; i <= 128; i++) t[i] += t[i - 1]; for (int i = n; i >= 1; i--) sa[t[a[i]]--] = i; int now = 128; for (int k = 1; k <= n; k *= 2) { int cnt = 0; for (int i = n - k + 1; i <= n; i++) b[++cnt] = i; for (int i = 1; i <= n; i++) if (sa[i] > k) b[++cnt] = sa[i] - k; memset(t, 0, sizeof(t)); for (int i = 1; i <= n; i++) t[a[i]]++; for (int i = 2; i <= now; i++) t[i] += t[i - 1]; for (int i = n; i >= 1; i--) sa[t[a[b[i]]]--] = b[i], b[i] = 0; swap(a, b); int tot = 1; a[sa[1]] = 1; for (int i = 2; i <= n; i++) { if (b[sa[i]] == b[sa[i - 1]] && b[sa[i] + k] == b[sa[i - 1] + k]) a[sa[i]] = tot; else a[sa[i]] = ++tot; } if (tot == n) break; now = tot; } } void gethi() { memset(rk, 0, sizeof(rk)); memset(h, 0, sizeof(h)); for (int i = 1; i <= n; i++) rk[sa[i]] = i; int now = 0; for (int i = 1; i <= n; i++) { if (rk[i] == 1) continue; if (now) now--; int j = sa[rk[i] - 1]; while (i + now <= n && j + now <= n && s[i + now] == s[j + now]) now++; h[rk[i]] = now; } for (int i = 1; i <= n; i++) h[i] = h[i + 1]; } ull cnt[N]; struct node { int l, r, hi; ull f[30]; int ql, qr; } p[N]; void bu(int d, int l, int r) { if (!d) return; p[d].ql = l, p[d].qr = r; bu(p[d].l, l, d); bu(p[d].r, d + 1, r); } int qq[N]; void dikaer() { int top = 0; h[n] = -1; for (int i = 1; i <= n; i++) { int now = 0; while (top && h[i] <= h[qq[top]]) { p[qq[top]].r = now; now = qq[top]; top--; } p[i].l = now; qq[++top] = i; } } void dfs(int x) { if (p[x].l) dfs(p[x].l); if (p[x].r) dfs(p[x].r); node zuo, you; if (!p[x].l) { zuo.ql = zuo.qr = p[x].ql; for (int i = 0; i <= 28; i++) zuo.f[i] = 0; zuo.f[s[sa[p[x].ql] - 1] - 'a' + 1] = 1; } else zuo = p[p[x].l]; if (!p[x].r) { you.ql = you.qr = p[x].qr; for (int i = 0; i <= 28; i++) you.f[i] = 0; you.f[s[sa[p[x].qr] - 1] - 'a' + 1] = 1; } else you = p[p[x].r]; ull ans = 0; for (int i = 0; i <= 28; i++) ans += zuo.f[i]; ull tmp = 0; for (int i = 0; i <= 28; i++) tmp += you.f[i]; ull da = 0; for (int i = 0; i <= 28; i++) da += zuo.f[i] * you.f[i]; cnt[h[x]] += ans * tmp - da; for (int i = 0; i <= 28; i++) p[x].f[i] = zuo.f[i] + you.f[i]; } int Sum(int l, int i) { if (l <= 0) return 0; int ans = (i + 1) * (1 + l) * l / 2; int tmp = l * (l + 1) * (2 * l + 1) / 6; ans -= tmp; return ans; } int k1, k2; void work() { dfs(p[n].l); ull ans = 0; for (int i = 1; i <= n; i++) { int k = 0; if (k1 + k2 >= i) k = Sum(min(k1, i), i) - Sum(i + 1 - k2 - 1, i); ans += 1uLL * k * cnt[i]; } cout << ans; } signed main() { cin >> s >> k1 >> k2; n = s.size(); s = " " + s; s[0] = 'a' - 1; getsa(); gethi(); dikaer(); bu(p[n].l, 1, n); work(); } ```