题目大意:给一个长度为$n$的字符串,求:
$$
sumlimits_{1leqslant i<jleqslant n}|suf_i|+|suf_j|-2 imes lcp(suf_i,suf_j)
$$
题解:建一棵后缀树,这个式子就成了后缀树上所有后缀之间的距离(后缀树可以把字符串反着加入后缀自动机得到的$fail$数组而来),然后有两种做法:
1. 把$sumlimits_{1leqslant i<jleqslant n}|suf_i|+|suf_j|$直接求出来
$$
egin{align*}
&sumlimits_{1leqslant i<jleqslant n}|suf_i|+|suf_j|\
=&sumlimits_{1leqslant i<jleqslant n}i+j\
=&dfrac{n(n+1)(n-1)} 2
end{align*}
$$
然后对每个点考虑它作为$lca$的贡献
2. 直接考虑每条边的贡献
卡点:无
C++ Code:(方法一)
#include <cstdio> #include <cstring> #include <algorithm> #define maxn 500010 namespace SAM { #define N (maxn << 1) int head[N], cnt; struct Edge { int to, nxt; } e[N]; inline void addedge(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; } int R[N], fail[N], nxt[N][26]; int lst = 1, idx = 1; int sz[N]; void append(char __ch) { int ch = __ch - 'a'; int p = lst, np = lst = ++idx; R[np] = R[p] + 1; sz[np] = 1; for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np; if (!p) fail[np] = 1; else { int q = nxt[p][ch]; if (R[q] == R[p] + 1) fail[np] = q; else { int nq = ++idx; std::copy(nxt[q], nxt[q] + 26, nxt[nq]); fail[nq] = fail[q], R[nq] = R[p] + 1, fail[np] = fail[q] = nq; for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq; } } } long long ans; void dfs(int u) { long long tmp = 0; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; dfs(v); tmp += static_cast<long long> (sz[u]) * sz[v]; sz[u] += sz[v]; } ans += 2 * tmp * R[u]; } long long work() { for (int i = 2; i <= idx; i++) addedge(fail[i], i); dfs(1); return ans; } #undef N } int n; char s[maxn]; long long ans; int main() { scanf("%s", s + 1); n = strlen(s + 1); for (int i = n; i; i--) SAM::append(s[i]); ans = static_cast<long long> (n - 1) * n * (n + 1) / 2; ans -= SAM::work(); printf("%lld ", ans); return 0; }
C++ Code:(方法二)
#include <cstdio> #include <cstring> #include <algorithm> #define maxn 500010 long long ans; int n; namespace SAM { #define N (maxn << 1) int head[N], cnt; struct Edge { int to, nxt; } e[N]; inline void addedge(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; } int R[N], fail[N], nxt[N][26]; int lst = 1, idx = 1; int sz[N]; void append(char __ch) { int ch = __ch - 'a'; int p = lst, np = lst = ++idx; R[np] = R[p] + 1; sz[np] = 1; for (; p && !nxt[p][ch]; p = fail[p]) nxt[p][ch] = np; if (!p) fail[np] = 1; else { int q = nxt[p][ch]; if (R[q] == R[p] + 1) fail[np] = q; else { int nq = ++idx; std::copy(nxt[q], nxt[q] + 26, nxt[nq]); fail[nq] = fail[q], R[nq] = R[p] + 1, fail[np] = fail[q] = nq; for (; p && nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq; } } } void dfs(int u) { for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; dfs(v); sz[u] += sz[v]; ans += static_cast<long long> (n - sz[v]) * (sz[v]) * (R[v] - R[u]); } } void work() { for (int i = 2; i <= idx; i++) addedge(fail[i], i); dfs(1); } #undef N } char s[maxn]; int main() { scanf("%s", s + 1); n = strlen(s + 1); for (int i = n; i; i--) SAM::append(s[i]); SAM::work(); printf("%lld ", ans); return 0; }