[洛谷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;
}

  

转载于:https://www.cnblogs.com/Memory-of-winter/p/10162064.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值