码迷,mamicode.com
首页 > 其他好文 > 详细

「SPOJ DYNALCA」Dynamic LCA

时间:2020-05-02 23:09:45      阅读:65      评论:0      收藏:0      [点我收藏+]

标签:mic   没有   hint   最近公共祖先   std   位置   for   roo   stream   

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

\(1\le n, m\le 10^5\)

Solution

看到加边和删边,不难想到用 \(\texttt{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

标签:mic   没有   hint   最近公共祖先   std   位置   for   roo   stream   

原文地址:https://www.cnblogs.com/-Wallace-/p/12819613.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!