LINK

使用 t a r j a n tarjan tarjan求出割点,显然非割点的答案是 2 ∗ ( n − 1 ) 2*(n-1) 2(n1)

如果是割点,割掉之后,会把原图分成若干个连通块

下面的讨论暂时不计算点 u u u的贡献

考虑在 t a r j a n tarjan tarjan形成的 d f s dfs dfs树中

u u u的儿子分别是 v 1 , v 2 , v 3 . . . v_1,v_2,v_3... v1,v2,v3...,子树大小分别是 s i z [ v 1 ] , s i z [ v 2 ] , s i z [ v 3 ] . . . siz[v_1],siz[v_2],siz[v_3]... siz[v1],siz[v2],siz[v3]...

这些儿子中,有些儿子的 l o w [ v ] > = d f n [ u ] low[v]>=dfn[u] low[v]>=dfn[u]

说明这些儿子不能通过非父回边到达更浅的节点,也就是被分割为一个连通块

那么直接计算这个儿子的贡献,为 s i z [ v ] ∗ ( n − 1 − s i z [ v ] ) siz[v]*(n-1-siz[v]) siz[v](n1siz[v])

设这类儿子的总大小为 s u m sum sum,那么这些点到不了其余 ( n − 1 − s u m ) (n-1-sum) (n1sum)个点

加上贡献,最后加上点 u u u的贡献 2 ∗ ( n − 1 ) 2*(n-1) 2(n1)即可

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e6+10;
int n,m,id,dfn[maxn],low[maxn],siz[maxn],ans[maxn],cut[maxn];
struct edge{
	int to,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v){ d[++cnt] = ( edge ){v,head[u]},head[u] = cnt; }
void tarjan(int u,int fa)
{
	dfn[u] = low[u] = ++id; siz[u] = 1;
	int child = 0,sum = 0;
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v = d[i].to;
		if( !dfn[v] )
		{
			child++; tarjan(v,fa);
			siz[u] += siz[v];
			if( low[v]>=dfn[u] )//发现割点 
			{
				ans[u] += siz[v]*( n-siz[v]-1 );
				sum += siz[v];
				if( u!=fa )	cut[u] = 1;
			}
			low[u] = min( low[u],low[v] );
		}
		else	low[u] = min( low[u],dfn[v] );
	}
	if( child>=2 && u==fa )	cut[u] = 1;
	if( !cut[u] )	ans[u] = 2*(n-1);
	else	ans[u] += ( n-sum-1 )*sum+2*(n-1);
}
signed main()
{
	cin >> n >> m;
	for(int i=1;i<=m;i++)
	{
		int l,r; cin >> l >> r;
		add(l,r); add(r,l);
	}
	tarjan(1,1);
	for(int i=1;i<=n;i++)	cout << ans[i] << endl;
}