P3469 [POI2008]BLO-Blockade

传送门

题目大意:
Byteotia 城市有 n 个城镇,m
条双向道路。每条道路连接两个不同的城镇,没有重复的道路,所有城镇连通。
输出 n个数,代表如果把第 i 个点去掉,将有多少对点不能互通。


这是一道求割点的经典题目,需要好好分析一下结果是怎么得来的。

首先我们要明确一点,这里如果(1,3)两点不能互通,那么(3,1)也是不能互通的,即得出来不能互通的点需要乘2。
还有一个注意点是我们去掉i点,不是说i点没有了,而是说连接i的边没有了,i还是单独的点,和另外(n-1)个点不相连。

我们先要根据搜索树来记录每棵树的儿子节点总数,如果去掉i点,那么可能分成多个块(因为每个节点都可能有多个儿子,而去掉i点后,每个孩子都会形成自己的块)。

对于形成的每个块,它里面的点和其它块中的点都没有连接;
假设有块1,块2,块3,那么他们所提供的边个数就是块1 * 块2+块1 * 块3+块2 * 块3;如果他们都是有x这点割开的子树,化简后那我们就可以记录成ans[x] +=size[i]*sum,sum是已经分开块的总点数,当只有块1,sum=0,当块2被分割,sum = 块1,当块3被分割,sum = 块1+块2。
这里我们求的ans[x] 是x以下的子树块,那么x的父亲块我们也是去不了的,
x的父亲块就可以写成(n-1-sum)(因为图连通), 父亲块所能提供的边就是 父亲块 * 所有子块的和 ,即 ans[x] += (n-1-sum) * sum;最后别忘了我们被割开的x点,他并不是消失了,只是没人要了,他可以提供 n-1 个点与它联系不到, 即ans[x] += n-1;最后最后,再把结果 *2即可;

这里网上很多版本,不过总的来说都是这样,有区分是不是割点判断的,我们这里不是割点sum == 0,就不会有问题,有的是先把x 这个点的影响放在里面的,我感觉不是太好理解,就放在外面,原理都一样。


#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+7,M = 1e6+7;
typedef long long ll;
int n,m;
int cnt,head[M],nex[M],ver[M];
int low[N],dfn[N],dfstime,cut[N];
void add(int x,int y){
	ver[++cnt] = y;
	nex[cnt] = head[x];
	head[x] = cnt;
}
void read(){
	scanf("%d%d",&n,&m);
	for(int i=1; i <= m;i++){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
}
int size[N]; ll ans[N];
void tarjan(int x,int root){
	dfn[x] = low[x] = ++dfstime;
	int tot = 0;
	size[x] = 1;
	ll sum = 0;
	for(int i=head[x];i;i=nex[i]){
		int y = ver[i];
		if(!dfn[y]){
			tot++;
			tarjan(y,root);
			low[x] = min(low[x],low[y]);
			size[x] += size[y]; //记录x的子树节点个数 
			if((x==root && tot>1) || (x!=root && low[y]>= dfn[x])){
				cut[x] = 1;
				ans[x] += size[y]*sum;  //所有子树能提供的边数 
										//x的子树size[y]能提供的个数*前面已经提供了同属于x子树的个数和 
				sum += size[y];//sum加上这颗子树点 
			}
		}else{
			low[x] = min(low[x],dfn[y]);
		}
	}
	ans[x] += (n-1-sum)*sum+(n-1); //父亲块能提供的边数 + x自己能提供的边数 
}
int main(){
	read();
	for(int i=1;i<=n;i++)
	if(!dfn[i]) tarjan(i,i);
	
	for(int i=1;i <= n;i++){
		printf("%lld\n",ans[i]*2);
	}
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值