题解:[POI2008] BLO-Blockade

洛谷P3469

Posted by TH911 on January 25, 2025

题目传送门

前置知识:Tarjan 求割点

不会可以看看

题意分析

给定 $n$ 个点,$m$ 条边的无向图,点从 $1$ 至 $n$ 标号。

求对于每一个点 $i$,删除该点后不连通的有序点对 $(x,y)$ 的个数。

保证给定图连通。

割点

因为原图连通,所以最开始时对于任意两个点都是连通的。

而删去某个点 $i$ 后如果存在点对 $(x,y)$ 满足点 $x$ 和点 $y$ 不连通,显然点 $i$ 是一个割点

不为割点时的答案

当点 $i$ 不为割点时,那么答案就是其他点和点 $i$ 不连通形成的点对,即 $2(n-1)$。

为割点时的答案

以求样例中点 $4$ 的答案为例。

首先点 $4$ 的答案 $14$ 对应的是以下几组(和反过来的):

编号 $x$ $y$
$1$ $1$ $4$
$2$ $1$ $5$
$3$ $2$ $4$
$4$ $2$ $5$
$5$ $3$ $4$
$6$ $3$ $5$
$7$ $4$ $5$

画一下样例的 DFS 生成树:

显然,点 $3,4$ 是割点。

删除点 $4$ 后,原图会断成两个连通块:$(1,3,4)$ 和 $(5)$。

如图:

用各个连通块乘起来即可。

具体而言,就是令连通块的个数分别为 $y_1,y_2,y_3,\cdots,y_k$,则答案为:

\[\sum_{i=1}^k y_i\times(n-y_i)\]

解释:连通块中的每一个点都不能到达连通块外的点,$n$ 为总点数。

因为 Tarjan 算法的本质就是个 DFS,我们可以在 Tarjan 的过程中来计算。

对于在 DFS 生成树上不是父节点的能够到达的节点,可以使用 $n-\sum\limits_{i=1}^ky_i-1$ 来求出该连通块大小。

但是需要注意的是,由于回溯边在 DFS 生成树上的存在,直接算答案可能会出问题。

以求点 $3$ 的答案为例,直接求会重复计算 $1,2$ 的答案,然而实际上 $1,2$ 是一个连通块。

解决方法就是令 $size_x$ 表示在 DFS 生成树上点 $x$ 的子树大小,$sonSize$ 表示当前点 $x$ 的不重复统计的子树大小。具体而言就是对于子节点 $v$,当且仅当 $low_v\geq dfn_x$ 时,才会将 $size_v$ 计入 $sonSize$ 内。

这样,就有效避免了重复计数。

注:$n-sonSize-1$ 表示的就是节点 $x$ 及其子树的大小。

AC 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#define int long long 
using namespace std;
const int N=100000,M=500000;
struct graph{
	struct edge{
		int v,r;
	}a[2*M+1];
	
	int h[N+1];
	void create(int u,int v){
		static int top;
		a[++top]={v,h[u]};
		h[u]=top;
	}
}g;
int n,m,ans[N+1];
void Tarjan(int x,int fx,int root){
	static int cnt,dfn[N+1],low[N+1],size[N+1];
	dfn[x]=low[x]=++cnt;
	size[x]=1;
	int son=0,pl=n-1,sonSize=0;
	for(int i=g.h[x];i;i=g.a[i].r){
		int &v=g.a[i].v;
		if(!dfn[v]){
			Tarjan(v,x,root);
			low[x]=min(low[x],low[v]);
			size[x]+=size[v];
			if(low[v]>=dfn[x]){
				son++;
				sonSize+=size[v];
				if(v!=fx){
					pl+=(n-size[v])*size[v];
				}
			}
		}else{
			low[x]=min(low[x],dfn[v]);
		}
	}sonSize++;
	pl+=(n-sonSize)*sonSize;
	bool cut=false;
	if(x==root){
		if(son>1)cut=true;
	}else{
		if(son>0)cut=true;
	}
	if(!cut)ans[x]=(n-1)<<1;
	else ans[x]=pl;
}
main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	scanf("%lld %lld",&n,&m);
	while(m--){
		int u,v;
		scanf("%lld %lld",&u,&v);
		g.create(u,v);
		g.create(v,u);
	}Tarjan(1,0,1);
	for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}