suxxsfe

一言(ヒトコト)

P3469 [POI2008]BLO-Blockade

tarjan 割点
P3469 [POI2008]BLO-Blockade

问题描述

B 城有 \(n\) 个城镇,\(m\) 条双向道路。
每条道路连结两个不同的城镇,没有重复的道路,所有城镇连通。
把城镇看作节点,把道路看作边,容易发现,整个城市构成了一个无向图。

输入格式

第一行包含两个整数 \(n\)\(m\)
接下来 \(m\) 行,每行包含两个整数 \(a\)\(b\),表示城镇 \(a\)\(b\) 之间存在一条道路。

输出格式

输出共 \(n\) 行,每行输出一个整数。
\(i\) 行输出的整数表示把与节点 \(i\) 关联的所有边去掉以后(不去掉节点 \(i\) 本身),无向图有多少个有序点 \((x,y)\),满足 \(x\)\(y\) 不连通。

数据范围

\(n\le 100000\)\(m\le500000\)


先用 tarjan 求出割点,对于非割点,除它以外的点的连通性不受他的影响,所以只有它与其它点会不连通,答案是 \(2(n-1)\)

对于那些是割点的点,它会把整个图分成若干个联通块(假设是 \(k\) 个),那么不连通的点对数就是

\[\sum_{i=1}^k a_i\cdot (n-a_i)+2(n-1) \]

就是说每个联通块的点数(\(a_i\)),都与其它点,除去当前这个割点(\(n-a_i-1\)),不相连通
当然这些联通块都是不包含当前这个割点的

那么如何求这些联通块?
tarjan 的时候,对于有向边 \((u,v)\)\(low_v\ge dfn_u\) 就说明如果不经过 \(u\)\(v\) 和它后面的点就不能回到 \(u\) 之前,那么搜索树中以 \(v\) 为根的子树,正是一个这样的联通快
假设这个搜索树子树的大小是 \(size_v\),那么这个 \(size_v\) 就是 \(a_i\),直接加到答案里就行了
\(\sum_{(u,v)}size_v=sum\),则还应该有一个大小为 \(n-sum-1\) 的联通块,就是在搜索树中从 \(u\)“往上”有一个联通块,那么答案还要再加 \((n-sum-1)\cdot (n-(n-sum-1)-1)=sum(n-sum-1)\)

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
//tarjan 割点 
#define N 100006
#define M 1000006
int n,m;
int fir[N],nex[M],to[M],tot;
int cut[N],dfn[N],low[N],dfscnt;
LL ans[N];
inline void add(int x,int y){
	to[++tot]=y;
	nex[tot]=fir[x];fir[x]=tot;
}
int tarjan(int u,int fa){//return size of subtree u
	dfn[u]=low[u]=++dfscnt;
	int children=0,size=1,size_v,sum=0;
	for(reg int v,i=fir[u];i;i=nex[i]){
		v=to[i];
		if(!dfn[v]){
			size_v=tarjan(v,fa);
			size+=size_v;
			low[u]=std::min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				sum+=size_v;ans[u]+=(LL)size_v*(n-size_v-1);
				if(u!=fa) cut[u]=1;
			}
			children++;
		}
		else low[u]=std::min(low[u],dfn[v]);
	}
	ans[u]+=(LL)sum*(n-sum-1);
	if(children>1&&u==fa) cut[u]=1;
//		std::printf("u : %d  size : %d ans : %d\n",u,size,ans[u]);
	return size;
}
int main(){
	n=read();m=read();
	for(reg int u,v,i=1;i<=m;i++){
		u=read();v=read();
		add(u,v);add(v,u);
	}
	tarjan(1,1);
	for(reg int i=1;i<=n;i++)
		std::printf("%lld\n",(n-1)*2+cut[i]*ans[i]);
//		
//		EN;EN;
//		for(reg int i=1;i<=n;i++) std::printf("dfn : %d  low : %d  size : %d\n",dfn[i],low[i],size[i]);
	return 0;
}
posted @ 2020-04-21 08:37  suxxsfe  阅读(114)  评论(0编辑  收藏  举报