【POI 2008】BLO

【题目】

传送门

Description

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

Input

输入两个数 n n n m m m n ≤ 100000 n≤100000 n100000 m ≤ 500000 m≤500000 m500000

Output

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

Sample Input

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

Sample Output

8
8
16
14
8


【分析】

这道题题目有误啊

分析一下样例,应该不是删掉 i i i,而是删掉所有与 i i i 相连的边

这道题应该还是比较显然的 Tarjan

先求割点,求的时候顺便求出搜索树中子树的大小 s i z e i size_i sizei

分以下的情况讨论:

  • 如果 x x x 不是割点,那么答案就是 2 ( n − 1 ) 2(n-1) 2(n1),就相当于只有 x x x 被隔开了,其他还是联通的
  • 如果 x x x 是割点,就找被它隔开的连通块的 s i z e i size_i sizei,产生的贡献就是 s i z e i ∗ ( n − s i z e i ) size_i*(n-size_i) sizei(nsizei),加起来就好

要注意的就是,如果发现有一颗子树可以连向 x x x 的祖先,就不能按照上面的算,要把它合到祖先的那部分算


【代码】

#include
#include
#include
#define N 100005
#define M 1000005
using namespace std;
int n,m,t,num;
int first[N],v[M],nxt[M];
int dfn[N],low[N],cut[N],size[N];
long long ans[N];
void add(int x,int y)
{
	t++;
	nxt[t]=first[x];
	first[x]=t;
	v[t]=y;
}
void Tarjan(int x)
{
	int i,j,child=0,sum=0;
	size[x]=1,dfn[x]=low[x]=++num;
	for(i=first[x];i;i=nxt[i])
	{
		j=v[i];
		if(!dfn[j])
		{
			child++,Tarjan(j);
			size[x]+=size[j];
			low[x]=min(low[x],low[j]);
			if(dfn[x]<=low[j])
			{
				sum+=size[j];
				ans[x]+=1ll*size[j]*(n-size[j]);
				if(x!=1||child>1)  cut[x]=1;
			}
		}
		else  low[x]=min(low[x],dfn[j]);
	}
	if(!cut[x])  ans[x]=(n-1)*2;
	else  ans[x]+=1ll*(n-sum-1)*(sum+1)+n-1;
}
int main()
{
	int x,y,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	Tarjan(1);
	for(i=1;i<=n;++i)
	  printf("%lld\n",ans[i]);
	return 0;
}

你可能感兴趣的:(#,连通性问题(Tarjan等))