根据割点的定义,若节点
i
i
i不是割点,就把节点i关联的所有边都去掉后,只有与其他
n
−
1
n - 1
n−1个节点之间是联通的。但是因为题目中
(
x
,
y
)
(x,y)
(x,y)与
(
y
,
x
)
(y,x)
(y,x)算不同的点对,所以答案为
2
∗
(
n
−
1
)
2*(n - 1)
2∗(n−1)。
若节点
i
i
i是割点,则把节点
i
i
i关联的边去掉后,图会分成若干个连通块。我们需要求出连通块的大小,两两相乘再相加。设在搜索树上,节点
i
i
i的子节点集合中,有
t
t
t个点
s
1
,
s
2
,
s
3
,
.
.
.
,
s
t
s_{1},s_{2},s_{3},...,s_{t}
s1,s2,s3,...,st满足割点的判定法则
d
f
n
[
i
]
≤
l
o
w
[
s
k
]
dfn[i]\leq low[s_{k}]
dfn[i]≤low[sk]。最后会变成至多
t
+
2
t + 2
t+2个连通块。
我们可以在tarjan算法深搜的时候,顺便求出每棵子树的大小。若
s
i
z
e
[
x
]
size[x]
size[x]为以x为根的子树大小。最后的答案很明显就是
∑
k
=
1
t
s
i
z
e
[
s
k
]
∗
(
n
−
s
i
z
e
[
s
k
]
]
)
+
1
∗
(
n
−
1
)
+
(
n
−
1
−
∑
k
=
1
t
s
i
z
e
[
s
k
]
)
∗
(
1
+
∑
k
=
1
t
s
i
z
e
[
s
k
]
)
\sum_{k = 1}^{t}size[s_{k}]*(n - size[s_{k}]])+1*(n-1)+(n-1-\sum_{k = 1}^{t}size[s_{k}])*(1+\sum_{k = 1}^{t}size[s_{k}])
∑k=1tsize[sk]∗(n−size[sk]])+1∗(n−1)+(n−1−∑k=1tsize[sk])∗(1+∑k=1tsize[sk]) 所以最后的代码就是
Code:
#include<bits/stdc++.h>#define ll long longusingnamespace std;constint N =1e5+10, M =5e5+10;struct edge {int y, nxt;}e[M *2];int lin[M], len =1;int dfn[N], low[N], num, size[N];int n, m;
ll ans[N];bool cut[N];inline ll read(){
ll s =0, f =1;char ch;for(; ch <'0'|| ch >'9'; ch =getchar())if(ch =='-') f =-1;for(; ch >='0'&& ch <='9'; ch =getchar()) s =(s <<1)+(s <<3)+ ch -'0';return s * f;}inlinevoidinsert(int xx,int yy){
e[++len]={yy, lin[xx]};
lin[xx]= len;
e[++len]={xx, lin[yy]};
lin[yy]= len;}inlinevoidtarjan(int x){
dfn[x]= low[x]=++num;
size[x]=1;int flag =0, sum =0;for(int i = lin[x]; i; i = e[i].nxt){int y = e[i].y;if(!dfn[y]){tarjan(y);
size[x]+= size[y];
low[x]=min(low[x], low[y]);if(low[y]>= dfn[x]){
flag++;
ans[x]+=(ll)size[y]*(n - size[y]);
sum += size[y];if(x !=1|| flag >1) cut[x]=1;}}else low[x]=min(low[x], dfn[y]);}if(cut[x]) ans[x]+=(ll)(n - sum -1)*(sum +1)+(n -1);else ans[x]=2*(n -1);}intmain(){
n =read();
m =read();for(int i =1; i <= m;++i){int xx =read(), yy =read();if(xx == yy)continue;insert(xx, yy);}tarjan(1);for(int i =1; i <= n;++i)printf("%lld\n", ans[i]);return0;}