[POI2008]BLO

[POI2008]BLO

一开始没看懂样例,连去掉的点都算……

显然如果i不是割点,那么ans[i]=(n-1)*2,

如果i是割点,ans[i]=将i去掉后生成的联通块大小两两相乘的和加上i本身,但这样并不好算,换一种思路,在求割点时记录搜索树中以x为根的子树大小,

(式子太长不想打了……)

#include<iostream>
#include<cstdio>
#define int long long
#define MAXN 100010
using namespace std;
struct edge
{
	int u,v,nxt;
	#define u(x) ed[x].u
	#define v(x) ed[x].v
	#define n(x) ed[x].nxt
}ed[MAXN*10];
int first[MAXN],num_e;
#define f(x) first[x]
int n,m;
inline void add(int u,int v)
{
	++num_e;
	u(num_e)=u;
	v(num_e)=v;
	n(num_e)=f(u);
	f(u)=num_e;
}
int dfn[MAXN],low[MAXN],size[MAXN],cnt,root;
bool iscut[MAXN];
int ans[MAXN];
void tarjan(int x)
{
	dfn[x]=low[x]=++cnt;
	int flag=0,sum=0;size[x]=1;
	for(int i=f(x);i;i=n(i))
	if(!dfn[v(i)])
	{
		tarjan(v(i)),low[x]=min(low[x],low[v(i)]);
		size[x]+=size[v(i)];
		if(low[v(i)]>=dfn[x])
		{
			flag++;
			ans[x]+=size[v(i)]*(n-size[v(i)]);sum+=size[v(i)];
			if(x!=root || flag>1)iscut[x]=1;	
		}
	}
	else low[x]=min(low[x],dfn[v(i)]);
	if(iscut[x])ans[x]+=(n-sum-1)*(sum+1)+n-1;
	else        ans[x] =(n-1)*2;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	int a,b;
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&a,&b);
		if(a!=b)add(a,b);add(b,a);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i]){root=i;tarjan(i);}
	for(int i=1;i<=n;i++)
		printf("%lld
",ans[i]);
}
原文地址:https://www.cnblogs.com/Al-Ca/p/11181966.html