[洛谷P3346][ZJOI2015]諸神眷顧的幻想鄉
阿新 • • 發佈:2018-12-22
題目大意:給你一棵$n$個點的樹,最多有$20$個葉子節點,問共有幾個不同的子串
題解:廣義$SAM$,對每個葉子節點深搜一次,每個節點的$lst$設為這個節點當時的父親,這樣就可以時建出來的$SAM$含有所有的字串
卡點:無
C++ Code:
#include <cstdio> #include <iostream> #define maxn 100010 int head[maxn], cnt; struct Edge { int to, nxt; } e[maxn << 1]; int ind[maxn]; inline void addedge(int a, int b) { e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt; e[++cnt] = (Edge) {a, head[b]}; head[b] = cnt; } int w[maxn]; namespace SAM { #define N (maxn * 22 << 1) int lst = 1, idx = 1; int R[N], fail[N], nxt[N][10]; void append(int ch) { int p = lst, np = lst = ++idx; R[np] = R[p] + 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[p] + 1 == R[q]) fail[np] = q; else { int nq = ++idx; R[nq] = R[p] + 1, fail[nq] = fail[q], fail[q] = fail[np] = nq; std::copy(nxt[q], nxt[q] + 10, nxt[nq]); for (; nxt[p][ch] == q; p = fail[p]) nxt[p][ch] = nq; } } } void dfs(int u, int fa = 0) { append(w[u]); int tmp = lst; for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to; if (v != fa) dfs(v, u), lst = tmp; } } long long query() { long long ans = 0; for (int i = 2; i <= idx; i++) ans += R[i] - R[fail[i]]; return ans; } #undef N } int n, m; int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", w + i); for (int i = 1, a, b; i < n; i++) { scanf("%d%d", &a, &b); addedge(a, b); ind[a]++, ind[b]++; } for (int i = 1; i <= n; i++) if (ind[i] == 1) { SAM::lst = 1; SAM::dfs(i); } printf("%lld\n", SAM::query()); return 0; }