题解:[POI2008] BLO-Blockade

题目传送门

前置知识: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 生成树:

038

显然,点 \(3,4\) 是割点。

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

如图:

039

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

具体而言,就是令连通块的个数分别为 \(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 代码

//#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;
}
posted @ 2025-07-21 19:43  TH911  阅读(27)  评论(0)    收藏  举报