tarjan求割点,求割开后各个连通块大小的乘积,可以考虑tarjan的过程。
只考虑DFS树上每个子树的贡献,考虑做到了第v棵子树,之前子树大小为sum,由于子树间不连通,ans+=sum*siz[v]
之后考虑祖先部分,ans+=sum*(n-sum-1),n-sum-1是祖先部分子树的大小
注意点对是无序的,所以答案要乘以2
#include<iostream> #include<cstdio> #define int long long using namespace std; inline int rd() { int ret=0,f=1; char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } const int MAXN=100005; int n,m; struct Edge { int next,to,w; } e[500005<<1]; int ecnt,head[MAXN]; inline void add(int x,int y) { e[++ecnt].next = head[x]; e[ecnt].to = y; head[x] = ecnt; } int dfn[MAXN],low[MAXN],tim; int siz[MAXN],ans[MAXN]; void tarjan(int x) { dfn[x]=low[x]=++tim; siz[x]=1; long long sum=0; for(int i=head[x]; i; i=e[i].next) { int v=e[i].to; if(!dfn[v]) { tarjan(v);siz[x]+=siz[v]; low[x]=min(low[x],low[v]); if(dfn[x]<=low[v]) { ans[x]+=sum*siz[v]; sum+=siz[v]; } } else{ low[x]=min(low[x],dfn[v]); } } ans[x]+=sum*(n-sum-1); } signed main() { n=rd(); m=rd(); int x,y,w; for(int i=1; i<=m; i++) { x=rd(); y=rd(); add(x,y); add(y,x); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) ans[i]+=n-1; for(int i=1;i<=n;i++) printf("%lld ",ans[i]<<1); return 0; }