🚣♂️ 🚣♂️ 🚣♂️
注意根节点的特判,对于根节点,我们先以计算割点的方式计算他的子树对答案的贡献(否则如果直接用2*(n-1)显然是错的),然后判断根节点是不是根节点之后再进行进一步计算
:DFS树可分为子树与父连通块俩部分考虑
int n,m;
vector<int>vc[MX];
int tot,dfn[MX],low[MX];
int cut[MX];// 判读某点是不是割点,是为true
ll ans[MX];//某割点的子树到其他点的点对,对答案的贡献
int size[MX];//dfs树中子树大小
void tarjan(int x,int fx,int root)//当前结点,根节点,父结点
{
dfn[x]=low[x]=++tot;
int child=0;
ll sum=0;
for(auto v:vc[x])
{
if(v==fx) continue;
++child;
if(dfn[v]==0)
{
tarjan(v,x,root);
size[x]+=size[v];
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x])//x不是根节点且v不通过x到达不了祖先//注意根节点也要计算ans,需要进入循环
{
ans[x]+=size[v]*(n-size[v]);
sum+=size[v];
if(x!=root) cut[x]=1;
}
if(x==root&&child>=2) //特判根节点//x是根节点且有两个以上的孩子
cut[x]=1;
}
else low[x]=min(low[x],dfn[v]);//此时x--->v即为一条返祖边
}
if(cut[x]) ans[x]+=(n-1-sum)*(sum+1)+(n-1);//
else ans[x]=2*(n-1);
}
signed main()
{
cin>>n>>m;
rpp(i,n) size[i]=1;
rpp(i,m)
{
int x,y;cin>>x>>y;
vc[x].push_back(y),vc[y].push_back(x);
}
rpp(i,n) if(dfn[i]==0) tarjan(i,i,i);
rpp(i,n) cout<<ans[i]<<endl;
stop;
return 0;
}