题目描述

给定一张有向图,保证 $1$ 号点可以到达所有点。求从 $1$ 号点出发,每个点能支配的点的个数 (包括自身)。

输入格式

第一行包含两个正整数 $n, m$ ($n \leq 2 \times 10^5; m \leq 3 \times 10^5$),表示点数和边数。

接下来的 $m$ 行,每行包含两个正整数 $u, v$,表示有一条从 $u$ 到 $v$ 的有向边。

输出格式

输出一行,包含 $n$ 个整数,表示每个点能支配的点的个数。

题解

题目即构造出原 (有向) 图的支配树,然后求每个点的子树大小。

支配树的相关理论可以参见便笺 "支配树及其应用"。

总时间复杂度 $O \left( n \log n \right)$ (最坏复杂度,即为路径压缩、按秩合并的并查集复杂度)。

代码

#include <bits/stdc++.h>

const int N = 200054, M = N * 12;

int V, E, Es = 0;
int to[M], next[M], p[N];
int first[N], inv_first[N], S_first[N];
int cnt = 0, o[N], id[N];
int S[N], I[N], ans[N];

inline void down(int &x, const int y) {x > y ? x = y : 0;}
inline void addedge(int u, int v, int *fst) {to[++Es] = v; next[Es] = fst[u]; fst[u] = Es;}

namespace U {
	int p[N], w[N], st[N];

	inline void init() {for (int i = 1; i <= V; ++i) w[i] = p[i] = i;}
	inline void update(int &x, const int y) {id[S[x]] > id[S[y]] ? x = y : 0;}

	int ancestor(int x) {
		int i = 0;
		for (; x != p[x]; st[++i] = x, x = p[x]);
		for (st[i + 1] = x; i; p[st[i]] = x, --i) update(w[st[i]], w[st[i + 1]]);
		return x;
	}

	inline int get_min(int x) {return ancestor(x), w[x];}
}

void dfs(int x) {
	int i, y; o[++cnt] = x, id[x] = cnt;
	for (i = first[x]; i; i = next[i])
		if (!id[y = to[i]]) p[y] = x, dfs(y);
}

void solve() {
	int i, j, x, y, r;
	dfs(1), assert(cnt == V), U::init();
	for (i = 1; i <= V; ++i) S[i] = i;
	for (j = V; j > 1; --j) {
		for (r = INT_MAX, i = inv_first[ x = o[j] ]; i; i = next[i])
			y = to[i], down(r, id[ id[y] < id[x] ? y : S[U::get_min(y)] ]);
		U::p[x] = p[x], S[x] = o[r], addedge(S[x], x, S_first);
		for (i = S_first[ x = o[j - 1] ]; i; i = next[i])
			r = U::get_min(y = to[i]), I[y] = (S[r] == x ? x : r);
	}
	for (; ++j <= V; ) x = o[j], I[x] != S[x] && (I[x] = I[I[x]]);
}

int main() {
	int i, u, v;
	scanf("%d%d", &V, &E);
	for (i = 0; i < E; ++i) scanf("%d%d", &u, &v), addedge(u, v, first), addedge(v, u, inv_first);
	solve();
	for (i = 1; i <= V; ++i) ans[i] = 1;
	for (i = V; i > 1; --i) v = o[i], ans[I[v]] += ans[v];
	for (i = 1; i <= V; ++i) printf("%d%c", ans[i], i == V ? 10 : 32);
	return 0;
}

坑1:注意在求半支配点的时候要满足 $id[z] > id[x]$,因此初始时要置所有点的半支配点为 $+ \infty$。

坑2:注意比较的时候使用的是 dfs 序而不是点的编号,还有就是半支配点是和生成外向树 (树形图) 有关的,不是恒定的;支配点只和有向图与起点有关,与生成树无关。