Processing math: 100%

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

0%

「POI 2008」BLO(Tarjan)

题目大意

「POI 2008」BLO(BZOJ 1123)

给定一个 n 个点 m 条边的无向图,问删去每个点的所有邻边以后图中有多少对结点(有序数对)不联通。

数据范围:n105,m5×105

思路分析

首先删去某个点的邻边之后,它和其他所有点都不联通,所以预先把答案加上 2n2

根据割点的定义,只有删掉割点才会使图有不联通。对于每一个结点 u,我们看它会使那些结点不联通。考虑求割点的过程,如果一个相邻的点 v 满足 low(v)==dfn(u),那么删掉 u 就切断了 v 的子树,也就是 v 的子树与上面不联通了。我们对于每个点维护它在 DFS 树上子树的大小,然后每次找到一组 low(v)==dfn(u) 我们就将 v 子树内部的点数乘上当前删掉 u 后已经不和 u 父亲联通的点数 s 的值贡献到 u 的答案中,然后再把 s 加上 v 的子树大小。最后,我们再把答案加上 s 乘上 ns1 的值(表示 u 下面的点和 u 上面的点不联通的组数)即可。不要忘记这题求的是有序数对,所以答案要乘以 2;并且根结点是否是割点也是需要特判的。

时间复杂度 O(n+m)

代码实现

1
#include <cstdio>
2
#include <vector>
3
#include <algorithm>
4
using namespace std;
5
6
const int maxn = 1e5;
7
typedef long long ll;
8
int n, m, tm, sz[maxn + 3], dfn[maxn + 3], low[maxn + 3];
9
vector<int> G[maxn + 3];
10
ll ans[maxn + 3];
11
bool cut[maxn + 3];
12
13
void add(int u, int v) {
14
	G[u].push_back(v);
15
}
16
17
void tarjan(int u, bool is_rt = true) {
18
	dfn[u] = low[u] = ++tm, sz[u] = 1;
19
	int cnt = 0, sum = 0;
20
	for (int i = 0, v; i < G[u].size(); i++) {
21
		v = G[u][i];
22
		if (!dfn[v]) {
23
			tarjan(v, false);
24
			low[u] = min(low[u], low[v]);
25
			sz[u] += sz[v], cnt++;
26
			if (low[v] == dfn[u]) {
27
				ans[u] += 1ll * sum * sz[v];
28
				sum += sz[v]; 
29
				cut[u] = true;
30
			}
31
		} else {
32
			low[u] = min(low[u], dfn[v]);
33
		}
34
	}
35
	if (is_rt) {
36
		cut[u] = cnt >= 2;
37
	}
38
	ans[u] += 1ll * sum * (n - sum - 1);
39
	if (!cut[u]) {
40
		ans[u] = 0;
41
	}
42
	ans[u] = 2 * (ans[u] + n - 1);
43
}
44
45
int main() {
46
	scanf("%d %d", &n, &m);
47
	for (int i = 1, u, v; i <= m; i++) {
48
		scanf("%d %d", &u, &v);
49
		add(u, v), add(v, u);
50
	}
51
	tarjan(1);
52
	for (int i = 1; i <= n; i++) {
53
		printf("%lld\n", ans[i]);
54
	}
55
	return 0;
56
}