1. 程式人生 > >[洛谷P3346][ZJOI2015]諸神眷顧的幻想鄉

[洛谷P3346][ZJOI2015]諸神眷顧的幻想鄉

題目大意:給你一棵$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;
}