题意:给定\(N(N<=100000)\)个点\(M(M<=500000)\)条边的无向联通图,求出删掉每个点之后,有多少对点不连通了.(x,y)和(y,x)是不同的点对.
分析:对于 非割点 (割点:删掉该点后使得图不连通的点),删掉之后,就相当于少了一个点而已,所以答案就是\(2*(n-1)\)
而对于割点来说,删掉之后,整个图可能会被分割成几个联通块,设割点u有k个子节点,那么答案为\(\sum_{v=1}^ksize[v]*(n-size[v])+(n-1)*1+(n-sum-1)*(sum+1)\).解释一下这个式子,前面一部分求和算的是子树v中的点与其它的点不连通的对数,\((n-1)*1\)这个算的是其它的点与割点不连通的对数,\((n-sum-1)*(sum+1)\)中sum表示该割点子节点中的点的和(不包括本身),所以这个式子算的是其它的点与割点及其子节点中的点不连通的个数.
所以我们只要任选一个点开始跑tarjan判断割点,同时记录size[u],sum等信息,同时还可以计算\(\sum_{v=1}^ksize[v]*(n-size[v])\)这个式子.
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
int x=0,o=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')o=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*o;
}
const int N=100005;
const int M=500005;
int tot,head[N],nxt[M*2],to[M*2];
int n,m,top,sum,timeclock,root;
int size[N],dfn[N],low[N],cut[N];
ll ans[N];
inline void add(int a,int b){
nxt[++tot]=head[a];head[a]=tot;to[tot]=b;
}
inline void tarjan(int u){
dfn[u]=low[u]=++timeclock;
int child=0,sum=0;size[u]=1;
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(!dfn[v]){
tarjan(v);
size[u]+=size[v];
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
ans[u]+=(ll)size[v]*(n-size[v]);
sum+=size[v];
++child;
if(u!=root||child>=2)cut[u]=1;
}
}
else low[u]=min(low[u],dfn[v]);
}
if(!cut[u])ans[u]=2*(n-1);
else ans[u]+=(ll)(n-sum-1)*(sum+1)+(n-1);
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i){
int a=read(),b=read();
add(a,b);add(b,a);
}
tarjan(1);
for(int i=1;i<=n;++i)printf("%lld\n",ans[i]);
return 0;
}