「SPOJ DYNALCA」Dynamic LCA

Posted -wallace-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「SPOJ DYNALCA」Dynamic LCA相关的知识,希望对你有一定的参考价值。

Description

有一个森林最初由 (n) 个互不相连的点构成

你需要处理以下 (m) 次操作:

link A B:添加从顶点A到B的边,使 (A) 成为 (B) 的子节点,其中保证 (A) 是一个根顶点,(A)(B) 在不同的树中。

cut A:切断点 (A) 到其父节点的边,保证 (A) 是一个非根节点。

lca A B:输出 (A)(B) 的最近共同祖先,保证 (A)(B) 在同一棵树中。

Hint

(1le n, mle 10^5)

Solution

看到加边和删边,不难想到用 ( exttt{Link-Cut Tree})

但难点在于求 LCA。

假设我们有这样一颗树,以 1 为根,并要求 lca 6 9

技术图片

先打通 6 到 根 的实链(红色边为实边),access(6)

技术图片

如果我们再 access(9),那么:

技术图片

可以发现,原来有一条实边变虚(蓝色边)了,而这条虚边的父结点正是所求的最近公共祖先。

结论,access(x) 后,再 access(y) 的过程中实变虚的最后一条边就是 LCA(x, y)

具体做法,可以在 access 函数中加入返回值会好做一些。

inline int access(int x) {
	register int y = 0;
	for (; x; x = fa[y = x]) splay(x), rc = y;
	return y;
}
inline int LCA(int x, int y) {
	return access(x), access(y);
}

注意,cut 操作不能 makrRoot,因为 LCA 这个东西是会受根结点的位置影响的,不可随意换根。而 link 由于 (x) 本来就是根,makeRoot 没有关系。

Code

#include <iostream>
#include <string>

using namespace std;
const int N = 1e5 + 5;

int ch[N][2], fa[N];
bool rev[N];

#define lc ch[x][0]
#define rc ch[x][1]

inline bool isRoot(int x) {
	return x != ch[fa[x]][0] && x != ch[fa[x]][1];
}
inline int getc(int x) {
	return x == ch[fa[x]][1];
}

inline void setRev(int x) {
	swap(lc, rc), rev[x] ^= 1;
}
inline void pushdown(int x) {
	if (rev[x]) {
		if (lc) setRev(lc);
		if (rc) setRev(rc);
		rev[x] = 0;
	}
}
inline void pushdownAll(int x) {
	if (!isRoot(x)) pushdownAll(fa[x]);
	pushdown(x);
}

inline void rotate(int x) {
	int y = fa[x], z = fa[y];
	int k = getc(x), w = ch[x][!k];
	if (!isRoot(y)) ch[z][getc(y)] = x;
	ch[x][!k] = y, ch[y][k] = w;
	if (w) fa[w] = y;
	fa[y] = x, fa[x] = z;
}
inline void splay(int x) {
	pushdownAll(x);
	for (register int y = fa[x]; !isRoot(x); rotate(x), y = fa[x])
		if (!isRoot(y)) rotate(getc(x) != getc(y) ? x : y);
}

inline int access(int x) {
	register int y = 0;
	for (; x; x = fa[y = x]) splay(x), rc = y;
	return y;
}
inline void makeRoot(int x) {
	access(x), splay(x), setRev(x);
}
inline void link(int c, int f) {
	makeRoot(c), fa[c] = f;
}
inline void cut(int x) {
	access(x), splay(x), fa[lc] = 0, lc = 0;
}
inline int LCA(int x, int y) {
	return access(x), access(y);
}

signed main() {
	int n, q;
	ios::sync_with_stdio(false);
	cin >> n >> q;
	
	for (; q; --q) {
		string cmd; int x, y;
		cin >> cmd;
		
		if (cmd == "lca")
			cin >> x >> y, cout << LCA(x, y) << endl;
		if (cmd == "link")
			cin >> x >> y, link(x, y);
		if (cmd == "cut")
			cin >> x, cut(x);
	}
	return 0;
}

以上是关于「SPOJ DYNALCA」Dynamic LCA的主要内容,如果未能解决你的问题,请参考以下文章

题解Luogu SP8791 DYNALCA - Dynamic LCA

SPOJ QTREE2 (LCA - 倍增 在线)

SPOJ 10628 Count on a tree (lca+主席树)

SPOJ:Ada and Orange Tree (LCA+Bitset)

SPOJ Count on a tree 主席树+lca

SPOJ COT Count on a tree(树上主席树 + LCA 求路径第k小)题解