[jzoj 2896] 战争游戏 {tarjan+割点}

题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


解题思路

判断割点,然后对于每一个割点。
设被割的联通分量为A,剩余为B
那么统计答案为:A到B的点对数+A点内的经过A点的点对数/2+A点到其它点(n-1)


代码

#include<cstdio>
#include<algorithm>
#define N 50010
using namespace std;
struct node{
	int y,next; 
}a[N*4];
int head[N],tot,num=0,n,m,ans[N],dfn[N],A,B,low[N],siz[N]; bool v[N]; 
void add(int x,int y){a[++tot]=(node){y,head[x]}; head[x]=tot;}
void tarjan(int x,int fa){
	dfn[x]=low[x]=++num; 
	int L=0; 
	for (int i=head[x];i;i=a[i].next){
		int y=a[i].y; v[y]=0; 
		if (!dfn[y]){
			tarjan(y,x); 
			siz[x]+=siz[y]; 
			if (low[y]>=dfn[x]) L+=siz[y]; 
			v[y]=1; 
		} 
		if (y!=fa) low[x]=min(low[x],low[y]); 
	}
	B=A=0;
	for (int i=head[x];i;i=a[i].next)
	if (low[a[i].y]>=dfn[x]&&v[a[i].y]){
		A+=(L-siz[a[i].y])*siz[a[i].y]; 
		B+=(n-L-1)*siz[a[i].y]; 
	}
	A/=2; 
	ans[x]=B+A+n-1; 
	return; 
}
int main(){
	scanf("%d%d",&n,&m); int x,y;
	for (int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); 
	for (int i=1;i<=n;i++) siz[i]=1; 
	tarjan(1,0); 
	for (int i=1;i<=n;i++) printf("%d\n",ans[i]); 
	return 0; 
}