BZOJ1123: [POI2008]BLO

BZOJ1123: [POI2008]BLO

Description

Byteotia城市有n个 towns m条双向roads. 每条 road 连接 两个不同的 towns ,没有重复的road. 所有towns连通。

Input

输入n<=100000 m<=500000及m条边

Output

输出n个数,代表如果把第i个点去掉,将有多少对点不能互通。

Sample Input

5 5
1 2
2 3
1 3
3 4
4 5

Sample Output

8
8
16
14
8

题解Here!

首先可以对任意一个节点$i$进行讨论:
1. 假如$i$不是割点,那么这个图只有$i$是独立在外面的。
由于求的是有序点对,所以除了$i$以外的$n-1$个点作为一个大的连通图对$i$加边,即为$2 imes(n-1)$对。
2. 假如$i$是割点,那么会把图分为$x$个连通块以及$i$本身。
由于$Tarjan$在求割点的过程中是一棵搜索树往下遍历,所以除了它和它的子树外,还会有其他剩余点共同构成另一个连通块。
设$i$所有子树的和为$w$,第$i$个子树的节点总数为$size[i]$,点对的数量便为:
$$sum_{i=1}^xsize[i] imes(n-size[x])+(n-1)+(1+w) imes(n-w-1)$$
所以在求割点的过程中每遇到一个$low[v]>=dfn[u]$便把对数加上$size[i] imes(n-size[i])$。
最后假如不是割点那直接把对数更新为$2 imes(n-1)$是割点则加上$n-1+(n-w-1) imes(w+1)$。
最后遍历输出答案即可。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 100010
using namespace std;
int n,m,c=1,d=1;
int head[MAXN],deep[MAXN],low[MAXN],size[MAXN];
bool cut[MAXN];
long long ans[MAXN];
struct Edge{
	int next,to;
}a[MAXN*10];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
inline void add_edge(int x,int y){
	a[c].to=y;a[c].next=head[x];head[x]=c++;
	a[c].to=x;a[c].next=head[y];head[y]=c++;
}
void dfs(int x){
	int s=0,w=0;
	deep[x]=low[x]=d++;
	size[x]=1;
	for(int i=head[x];i;i=a[i].next){
		int v=a[i].to;
		if(!deep[v]){
			dfs(v);
			low[x]=min(low[x],low[v]);
			size[x]+=size[v];
			if(low[v]>=deep[x]){
				ans[x]+=1LL*size[v]*(n-size[v]);
				w+=size[v];
				s++;
				if(x!=1||s>=2)cut[x]=true;
			}
		}
		else low[x]=min(low[x],deep[v]);
	}
	if(!cut[x])ans[x]=2*(n-1);
	else ans[x]+=1LL*(n-w-1)*(w+1)+n-1;
}
void work(){
	int x,y;
	n=read();m=read();
	for(int i=1;i<=m;i++){
		x=read();y=read();
		add_edge(x,y);
	}
	dfs(1);
	for(int i=1;i<=n;i++)printf("%lld
",ans[i]);
}
int main(){
	work();
    return 0;
}
原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9840474.html