强连通分量
l
o
w
[
u
]
low[u]
low[u]表示顶点u及其子树中的点连接的所有点的
d
f
n
dfn
dfn的最小值。
在
d
f
s
dfs
dfs过程中把沿途的点都入栈。在更新一个点的
l
o
w
low
low值的时候,一定是该点被它能到达的点给更新。当遇到一个还在栈里的点,相当于是走出来了一个
ρ
\rho
ρ型。那么这个环就构成了一组强连通分量。它们两两之间一定可以相互到达。而这个环的尾巴上的点的
l
o
w
low
low值是被它撞上的点的
d
f
n
dfn
dfn更新的,如下:
else if(ins[v]) low[u]=min(low[u],dfn[v]);
这是因为这个环的尾巴上的这个点不一定能到达外面那条链上的点。(看 ρ \rho ρ的形状)
而如果遇到一个点, d f n dfn dfn值不为 0 0 0,而又不在栈里,说明它已经被归到一个强连通分量里面了。就不管它。
如果遇到一个点, d f n dfn dfn值为 0 0 0,即还没有搜过,就搜它。搜完之后用它的 l o w low low值去更新当前点的 l o w low low值。
栈的顶端一部分存下的是一个强连通图。走到死胡同的时候相当于一个点就是一个强连通图了。
void tarjan(int u){
st[++top]=u,ins[u]=1;
dfn[u]=low[u]=++sz;
for(int i=Head[u];i;i=Next[i]){
int v=V[i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(ins[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int j;tot+=1;
while(j!=u){
j=st[top--];
ins[j]=0;
belong[j]=tot;
W[tot]+=val[j];
}
}
}
割点
首先,“根节点有n棵子树”这句话,是说这n棵子树是独立的,没有根节点不能互相到达。因此n不一定等于与根节点相邻的顶点数。因此加入了vis[v]为false的条件,因为如果(u, v1)和(u, v2)在一棵子树里,对v1进行DFS,一定能去到v2,vis[v2]就会为true,此时就不会children++了。
对于边(u, v),如果low[v]>=dfn[u],即v即其子树能够(通过非父子边)回溯到的最早的点,最早也只能是u,要到u前面就需要u的回边或u的父子边。也就是说这时如果把u去掉,u的回边和父子边都会消失,那么v最早能够回溯到的最早的点,已经到了u后面,无法到达u前面的顶点了,此时u就是割点。
例题
选中的点是去掉与它相连的所有边。
每个被隔离的点的答案有三部分
①它与其他点
n
−
1
n-1
n−1个点,这
n
−
1
n-1
n−1对不能相连。
②若干独立的强连通图。不同的图的内部点之间不能互相到达。
③所有强连通图中的点不能到达其他点。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
const int maxm=1e6+10;
int Head[maxn],Next[maxm],V[maxm],cnt=0;
int n,m,u,v,dfn[maxn],low[maxn],cut[maxn],size[maxn],tot=0;
inline void add(int u,int v){Next[++cnt]=Head[u],V[cnt]=v,Head[u]=cnt;}
ll ans[maxn];
void tarjan(int u,int sum=0){
size[u]=1,dfn[u]=low[u]=++tot;
for(int i=Head[u];i;i=Next[i]){
if(!dfn[V[i]]){
tarjan(V[i]),low[u]=min(low[u],low[V[i]]),size[u]+=size[V[i]];
if(low[V[i]]>=dfn[u])ans[u]+=1ll*sum*size[V[i]],sum+=size[V[i]];
}
else low[u]=min(low[u],dfn[V[i]]);
}ans[u]+=1ll*sum*(n-sum-1);
}
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;++i) u=read(),v=read(),add(u,v),add(v,u);
tarjan(1);for(int i=1;i<=n;++i) printf("%lld\n",(ans[i]+n-1)*2);
}