只要你跑的够快,锅就追不上你

0%

「学习笔记」支配树与 Lengauer Tarjan 算法

定义

对于一张有向图,确定一个根,如果根到 $x$ 的每条路径都经过 $y$,那么称 $y$ 是 $x$ 的支配点。求出原图的一个 dfs 树,那么 $x$ 的支配点一定在 $x$ 到根的链上。如果每个点向自己深度最大的支配点(记作 $\text{idom}(u)$)连边,就构成了支配树。

有向图 dfs 树的性质

  1. 图中所有两个端点没有祖先后代关系的非树边都从 dfn 大的点指向 dfn 小的点。
  2. 若 u 和 v 满足 u 的 dfn 比 v 的 dfn 小,那么图中任意从 u 到 v 的路径一定包含至少一个 u 和 v 的公共祖先。

算法流程

初步分析

对于 DAG,可以直接 LCA 做,复杂度 $O(n \log n)$。下面介绍对于所有图适用的算法:Lengauer Tarjan 算法,它的时间复杂度是 $O(n\cdot\alpha(n))$。

如果 $y$ 不是 $x$ 的后代,且满足存在 $y$ 到 $x$ 的一条路径,使得路径上的点(除 $x, y$)dfn 都大于 $\text{dfn}(x)$,那么称 $y$ 是 $x$ 的一个半支配点。可以证明 $x$ 的半支配点一定是 $x$ 的祖先,我们记 $\text{sdom}(x)$ 为 $x$ 半支配点中深度最小的点。

我们考虑先求出 $\text{sdom}$ 后再求出 $\text{idom}$。

求半支配点

考虑枚举路径上 $x$ 之前的那个点 $y$(满足 $\text{dfn}(y) > \text{dfn}(x)$)。$x$ 的半支配点是某个 $z$ 点的半支配点,其中 $z$ 是 $y$ 的祖先且 $\text{dfn}(z) > \text{dfn}(x)$。我们要求的就是 $z$ 点的最小值。考虑把所有点按照 $\text{dfn}(x)$ 从大到小排序,一一加入一个维护树结构的并查集,并查集中的每个点维护它到当前根的最小值即可。

求支配点

对于某点 $x$,记 $P_x$ 表示对于 $x$ 祖先链上的每个 $y$,树上路径 $\text{semi}(y) \rightarrow y$ 的并集。找到 $P_x$ 中深度最小的点 $z$,则:

  • semi(x) = semi(z) 时,idom(x) = semi(x);
  • 否则,idom(x) = idom(z)。

类似地,将点按照 $\text{semi}(x)$ 的深度从大到小排序,使用带权并查集维护即可。

代码实现

1
// Luogu P5180 支配树模版
2
#include <bits/stdc++.h>
3
using namespace std;
4
5
namespace __main__ {
6
	const int maxn = 2e5;
7
	int n, m, tot, dfn[maxn + 3], o[maxn + 3], dep[maxn + 3];
8
	int fa[maxn + 3], num[maxn + 3], lnk[maxn + 3], cnt[maxn + 3];
9
	int sdom[maxn + 3], idom[maxn + 3], ans[maxn + 3];
10
	vector<int> G[maxn + 3], T[maxn + 3], B[maxn + 3], D[maxn + 3];
11
12
	void add(int u, int v) {
13
		G[u].push_back(v);
14
		B[v].push_back(u);
15
	}
16
17
	void dfs(int u) {
18
		dfn[u] = ++tot, o[tot] = u;
19
		D[dep[u]].push_back(u);
20
		for (int i = 0, v; i < G[u].size(); i++) {
21
			v = G[u][i];
22
			if (dfn[v]) continue;
23
			T[u].push_back(v);
24
			dep[v] = dep[u] + 1;
25
			dfs(v);
26
		}
27
	}
28
29
	int min_1(int a, int b) {
30
		if (!a || !b) return a | b;
31
		return dfn[a] < dfn[b] ? a : b;
32
	}
33
34
	int min_2(int a, int b) {
35
		return dfn[sdom[a]] < dfn[sdom[b]] ? a : b;
36
	}
37
38
	int find(int x) {
39
		if (fa[x] == x) {
40
			return x;
41
		}
42
		int y = fa[x];
43
		fa[x] = find(y);
44
		if (y != fa[x]) {
45
			num[x] = min_2(num[x], num[y]);
46
		}
47
		return fa[x];
48
	}
49
50
	void main() {
51
		scanf("%d %d", &n, &m);
52
		for (int u, v, i = 1; i <= m; i++) {
53
			scanf("%d %d", &u, &v);
54
			add(u, v);
55
		}
56
		dep[1] = 1;
57
		dfs(1);
58
		for (int i = 1; i <= n; i++) {
59
			fa[i] = i;
60
		}
61
		for (int i = n, u; i; i--) {
62
			u = o[i];
63
			for (int j = 0, v; j < B[u].size(); j++) {
64
				v = B[u][j];
65
				sdom[u] = min_1(sdom[u], v);
66
				find(v);
67
				sdom[u] = min_1(sdom[u], min_1(sdom[num[v]], sdom[num[fa[v]]]));
68
			}
69
			num[u] = u;
70
			for (int j = 0; j < T[u].size(); j++) {
71
				fa[T[u][j]] = u;
72
			}
73
		}
74
		for (int i = 1; i <= n; i++) {
75
			cnt[dep[sdom[i]]]++;
76
		}
77
		for (int i = n; i; i--) {
78
			cnt[i] += cnt[i + 1];
79
		}
80
		for (int i = 1; i <= n; i++) {
81
			o[cnt[dep[sdom[i]]]--] = i;
82
		}
83
		for (int i = 1; i <= n; i++) {
84
			fa[i] = i;
85
		}
86
		int p = n + 1;
87
		for (int i = 1, u; i <= n; i++) {
88
			u = o[i];
89
			while (p > dep[sdom[u]]) {
90
				p--;
91
				for (int j = 0, v; j < D[p].size(); j++) {
92
					v = D[p][j], num[v] = v;
93
					for (int k = 0; k < T[v].size(); k++) {
94
						fa[T[v][k]] = v;
95
					}
96
				}
97
			}
98
			find(u);
99
			if (sdom[num[u]] == sdom[u]) {
100
				idom[u] = sdom[u];
101
			} else {
102
				lnk[u] = num[u];
103
			}
104
		}
105
		idom[1] = 0;
106
		for (int i = 1; i <= n; i++) {
107
			for (int j = 0, u; j < D[i].size(); j++) {
108
				u = D[i][j];
109
				if (!idom[u]) {
110
					idom[u] = idom[lnk[u]];
111
				}
112
			}
113
		}
114
		for (int i = 1; i <= n; i++) {
115
			ans[i] = 1;
116
		}
117
		for (int i = n; i; i--) {
118
			for (int j = 0, u; j < D[i].size(); j++) {
119
				u = D[i][j];
120
				ans[idom[u]] += ans[u];
121
			}
122
		}
123
		for (int i = 1; i <= n; i++) {
124
			printf("%d%c", ans[i], " \n"[i == n]);
125
		}
126
	}
127
}
128
129
int main() {
130
	__main__::main();
131
	return 0;
132
}

参考文献