[BZOJ1123]:[POI2008]BLO(塔尖)

题目传送门


题目描述

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


输入格式

输入nmm条边。


输出格式

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


样例

样例输入:

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

样例输出:

8
8
16
14
8


题解

看到这道题,应该想到缩点。

首先,如果一个点不是割点,那么把它去掉,不会影响与它无关的点对,那么,去掉它之后,减少的点对的个数即为2n-2

如果这个点是割点,那么去掉它,减少的点对个数不止2n-2,还需要将所有与他它通的联通块的大小两两相乘再相加。

考虑塔尖,在深度优先遍历的同时就能够完成统计答案的工作。

那么,删除一个割点后,减少的点对数量即为:

$ size[ s_{1} ]×(n-size[ s_{1} ])+size[ s_{2} ]×(n-size[ s_{2} ])+...+size[ s_{t} ]×(n-size[ s_{t} ])+n-1+(n-1- \sum \limits_{k=1}^{t} size[ s_{k} ])×(1+ \sum \limits_{k=1}^{t} size[ s_{k} ])$


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec
{
	int nxt;
	int to;
}e[1000001];
int head[100001],cnt;
int n,m;
int dfn[100001],low[100001],size[100001],tot;
long long ans[100001];
bool cut[100001];
void add(int x,int y)
{
	e[++cnt].nxt=head[x];
	e[cnt].to=y;
	head[x]=cnt;
}
void tarjan(int x)
{
	dfn[x]=low[x]=++tot;
	int flag=0,sum=0;
	size[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		if(!dfn[e[i].to])
		{
			tarjan(e[i].to);
			size[x]+=size[e[i].to];
			low[x]=min(low[x],low[e[i].to]);
			if(low[e[i].to]>=dfn[x])
			{
				flag++;
				sum+=size[e[i].to];//用来最后的∑
				ans[x]+=1LL*size[e[i].to]*(n-size[e[i].to]);//先统计进去
				if(x!=1||flag>1)cut[x]=1;
			}
		}
		else low[x]=min(low[x],dfn[e[i].to]);
	}
	if(cut[x])ans[x]+=1LL*(n-sum-1)*(sum+1)+n-1;//如果是割点
	else ans[x]=2*n-2;//如果不是割点
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		if(x==y)continue;
		add(x,y);
		add(y,x);
	}
	tarjan(1);
	for(int i=1;i<=n;i++)
		printf("%lld\n",ans[i]);
	return 0;
}

rp++

posted @ 2019-07-15 06:40  HEOI-动动  阅读(195)  评论(1编辑  收藏  举报