bzoj5084 hashit 广义SAM+树链的并

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=5084

题解

考虑平常对于静态问题,我们应该如何用 SAM 求本质不同的子串个数。

对于一个常规的 SAM,这个东西应该是 (sumlimits_{iin V} len_i - len_{fa_i})

很容易发现,我们如果把这个字符串每一个时刻的前一个字符和后一个字符给连接起来,这是一个树的关系。

考虑对这个树建立一棵广义 SAM。

但是上面的结论在广义 SAM 中不适用。不适用的是 (i) 的条件。

如果固定了当前的串是树上的哪一条链,这里就不应该是 (i in V) 了,而是 (i) 代表的子串(等价类)在这个串中出现过。

这个东西显然就是对于这个串,每一个前缀所在的 (endpos) 集合的等价类的点在 parent 树上的链的并的长度了。

因为每一次的添加或删除字符影响的只有一个点,所以维护 (parent) 树上的树链的并来实现。


时间复杂度 (O(nlog n))

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b , 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 2e5 + 7;

int n, Q, nod, dfc;
ll ans = 0;
char s[N], v[N];
int id[N], fa[N], ip[N], dis[N];
int f[N], dep[N], siz[N], son[N], dfn[N], pre[N], top[N];

struct Edge { int to, ne, w; } g[N]; int head[N], tot;
inline void addedge(int x, int y, int z) { g[++tot].to = y, g[tot].w = z, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int z) { addedge(x, y, z), addedge(y, x, z); }

struct Node { int c[26], fa, len; } t[N];
inline int extend(int p, int x) {
	if (t[p].c[x]) {
		int q = t[p].c[x];
		if (t[q].len == t[p].len + 1) return q;
		int nq = ++nod;
		t[nq] = t[q], t[nq].len = t[p].len + 1, t[q].fa = nq;
		for (; t[p].c[x] == q; p = t[p].fa) t[p].c[x] = nq;
		return nq;
	}
	int np = ++nod;
	t[np].len = t[p].len + 1;
	for (; p && !t[p].c[x]; p = t[p].fa) t[p].c[x] = np;
	// dbg("p = %d, np = %d, nod = %d, x = %c
", p, np, nod, x);
	if (!p) t[np].fa = 1;
	else {
		int q = t[p].c[x];
		if (t[q].len == t[p].len + 1) t[np].fa = q;
		else {
			int nq = ++nod;
			t[nq] = t[q], t[nq].len = t[p].len + 1, t[q].fa = t[np].fa = nq;
			for (; p && t[p].c[x] == q; p = t[p].fa) t[p].c[x] = nq;
		}
	}
	// dbg("p = %d, np = %d, nod = %d, x = %c
", p, np, nod, x);
	assert(!t[1].fa);
	return np;
}

inline void dfs1(int x, int fa = 0) {
	f[x] = fa, dep[x] = dep[fa] + 1, siz[x] = 1;
	for fec(i, x, y) if (y != fa) dfs1(y, x), siz[x] += siz[y], siz[y] > siz[son[x]] && (son[x] = y);
}
inline void dfs2(int x, int pa) {
	top[x] = pa, dfn[x] = ++dfc, pre[dfc] = x;
	if (!son[x]) return; dfs2(son[x], pa);
	for fec(i, x, y) if (y != f[x] && y != son[x]) dfs2(y, y);
}
inline int lca(int x, int y) {
	while (top[x] != top[y]) dep[top[x]] > dep[top[y]] ? x = f[top[x]] : y = f[top[y]];
	return dep[x] < dep[y] ? x : y;
}

inline void build() {
	ip[1] = nod = 1;
	for (int i = 2; i <= n; ++i)
		ip[i] = extend(ip[fa[i]], v[i] - 'a');
	for (int i = 2; i <= nod; ++i) addedge(t[i].fa, i, t[i].len - t[t[i].fa].len), dis[i] = t[i].len;
	// for (int i = 1; i <= nod; ++i) dbg("i = %d, t[i].fa = %d, t[i].len = %d
", i, t[i].fa, t[i].len);
}

struct cmp {
	inline bool operator () (const int &x, const int &y) { return dfn[x] < dfn[y]; }
};
std::set<int, cmp> st;

inline void ins(int x) {
	std::set<int, cmp>::iterator p = st.lower_bound(x);
	int y = 0, z = 0;
	ans += dis[x];
	if (p != st.end()) y = *p, ans -= dis[lca(x, y)];
	if (p != st.begin()) z = *--p, ans -= dis[lca(x, z)];
	if (y && z) ans += dis[lca(y, z)];
	st.insert(x);
}

inline void del(int x) {
	st.erase(x);
	std::set<int, cmp>::iterator p = st.lower_bound(x);
	int y = 0, z = 0;
	ans -= dis[x];
	if (p != st.end()) y = *p, ans += dis[lca(x, y)];
	if (p != st.begin()) z = *--p, ans += dis[lca(x, z)];
	if (y && z) ans -= dis[lca(y, z)];
}

inline void work() {
	build();
	dfs1(1), dfs2(1, 1);
	int now = 1;
	for (int i = 1; i <= Q; ++i) {
		if (s[i] == '-') del(ip[now]), now = fa[now];
		else now = id[i], ins(ip[now]);
		// dbg("now = %d, ip = %d
", now, ip[now]);
		printf("%lld
", ans);
	}
}

inline void init() {
	scanf("%s", s + 1);
	Q = strlen(s + 1);
	int now = n = 1;
	for (int i = 1; i <= Q; ++i) {
		if (s[i] == '-') now = fa[now];
		else fa[++n] = now, v[n] = s[i], now = n;
		id[i] = now;
	}
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
原文地址:https://www.cnblogs.com/hankeke/p/bzoj5084.html