题解:
这道题很容易看出是求割点的板子题,但是难点是计算贡献。
其实手推一下样例就可以发现,如果一个点不是割点的话那么割掉它的贡献就是n-1,因为少了n-1次拜访。
而如果它是割点怎么办呢?就可以这样做,它的贡献就是它的子树的大小size,子树上有size个点,而断掉它,这size个点的贡献为size*(n-size)。而这个点本身的贡献为n-1,还要算不是它的子树上的点的贡献,一共还剩下n-size-1个点,所以贡献同子树的贡献,也就是(n-size-1)*(size+1)的贡献,最后累加起来求和就可以了。
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+7; struct node{ int nxt,to; }edge[maxn*2]; int head[maxn],cnt; void add(int x,int y){ edge[++cnt].nxt=head[x]; edge[cnt].to=y; head[x]=cnt; } int n,m,x,y; int dfn[maxn],low[maxn],in; bool check[maxn]; long long ans[maxn]; long long siz[maxn]; void tarjan(int x){ dfn[x]=low[x]=++in; siz[x]=1; int son=0; long long sum=0; bool cut=false; for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].to; if(!dfn[v]){ tarjan(v); low[x]=min(low[v],low[x]); siz[x]+=siz[v]; if(low[v]>=dfn[x]){ son++; ans[x]+=(long long)siz[v]*(n-siz[v]); sum+=siz[v]; if(x!=1||son>=2) cut=true; } } else low[x]=min(dfn[v],low[x]); } if(!cut) ans[x]=(n-1)*2; else ans[x]+=(n-1)+(n-sum-1)*(sum+1); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y);add(y,x); } tarjan(1); for(int i=1;i<=n;i++) printf("%lld ",ans[i]); return 0; }