给定一张无向图,求每个点被封锁之后有多少个有序点对(x,y)(x!=y,1<=x,y<=n)满足x无法到达y
首先不是割点答案为2*(n-1),是割点就要考虑删掉割点会分开哪些连通块
考虑tarjan的过程,核心是对搜索树的处理,如果是割点的话删除掉点x会产生的连通块为x的所有以儿子为根的子树和x子树外的所有部分,每个子树产生的贡献为$size[s1]*(n-size[s1])$,x子树外的贡献为$(size[x])*(n-size[x])$,以及x自己的为$(n-1)$
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100009;
const int maxm=500009;
int n,m,tim,root;
struct node{
int v,nxt;
}e[maxm<<1];
int head[maxn],cnt=1;
inline void add(int u,int v){
e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;
}
int dfn[maxn],low[maxn];bool cut[maxn];
ll ans[maxn],sz[maxn];
void tarjan(int x){
dfn[x]=low[x]=++tim;sz[x]=1;
int son=0,sum=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].v;
if(!dfn[y]){
tarjan(y);sz[x]+=sz[y];
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
son++;
ans[x]+=sz[y]*(n-sz[y]);//每个点只能到当前子树内的点
sum+=sz[y];
if(x!=root||son>1)cut[x]=1;
}
}
else low[x]=min(low[x],dfn[y]);
}
if(cut[x])ans[x]+=1ll*(n-sum-1)*(sum+1)+(n-1);//x子树外的不能到x子树内的且x不能到其他所有点
else ans[x]=2*(n-1);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);if(u==v)continue;
add(u,v);add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])root=i,tarjan(i);
for(int i=1;i<=n;i++)printf("%lld\n",ans[i]);
}