====== 连通分量 ======
===== 强连通分量 =====
只有有向图才有这种定义,强连通分量中,点两两可达。
SCC 主要用于缩点,每一个点仅属于一个 SCC。缩点后是一个 DAG ,在建新图过程中需要注意两点不应属于同一 SCC 。
在实现中,在遍历到 $p\rightarrow q$ 返祖边时,对于 ''%%low[p]%%'' 的更新, ''%%min(low[p],low[q])%%'' 和 ''%%min(low[p],dfn[q])%%'' 等价,均可正确计算出结果。这与点双连通分量是不同的。
模板题:[[https://www.luogu.com.cn/problem/P3387|缩点]]
int sta[N],top;
int col[N],num;
int dfn[N],low[N],tot;
int vis[N];
void tarjan(int p){
dfn[p]=low[p]=++tot;
int i,q;
sta[++top]=p;vis[p]=1;
for(i=hd[p];i;i=e[i].n){
q=e[i].e;
if(!dfn[q])tarjan(q),low[p]=min(low[p],low[q]);
else if(vis[q])low[p]=min(low[p],low[q]);
}
if(dfn[p]==low[p]){
num++;
while(sta[top+1]!=p){
q=sta[top];
col[q]=num;
vis[q]=0;
top--;
}
}
}
\\
===== 点双连通分量 =====
一个点双删掉任意点后图仍然连通。注意,根据此定义,K1 和 K2 均为点双。
v-DCC 主要用于判割点,每一个非割点仅属于一个 v-DCC ,割点可以同时属于多个 v-DCC 。
在实现中,如果某个点 p 是割点,它应当满足以下条件之一:
- p 是根节点,且以它为根的 dfs 树有大于一个分支;
- p 不是根节点,且它的某个孩子 q 不能返祖(即 ''%%low[q]>=dfn[p]%%'' )。
由于求 DCC 是通过某个有孩子的割点进行判定的,故需要对孤立点进行特判。
模板题:[[http://acm.hdu.edu.cn/showproblem.php?pid=3394|点双]] [[https://www.luogu.com.cn/problem/P3388|割点]]
int low[N],dfn[N],tot;
int sta[N],top;
int cut[N];
vectordcc[N];int dcc_cnt;
void tarjan(int p,int f){
int q,i,child=0;
sta[++top]=p;
dfn[p]=low[p]=++tot;
for(i=hd[p];i;i=e[i].n){
q=e[i].e;
if(q==f)continue; // avoid return to fa
if(!dfn[q]){
tarjan(q,p);
low[p]=min(low[p],low[q]);
if(low[q]>dfn[p]);// i is cut-edge
if(low[q]>=dfn[p]){
// p is cut of branch q
if(f!=-1)cut[p]=1;
dcc[++dcc_cnt].pb(p);
int r;
do{
r=sta[top--];
dcc[dcc_cnt].pb(r);
}while(r!=q);
}
child++;
}
low[p]=min(low[p],dfn[q]);
}
if(f==-1){ // edge case: p is root
if(child>1)cut[p]=1; // 2 branch in dfs tree
if(hd[p]==0)dcc[++dcc_cnt].pb(p); // single point
}
}
\\
===== 边双连通分量 =====
一个边双删掉任意边后图仍然连通。
无向图中的 e-DCC 和有向图中的 SCC 很相似,有的时候可以进行缩点。
在实现中, e-DCC 和 SCC 的实现方式也类似,只是加了一个不能走父边的限制。
模板题:[[https://www.luogu.com.cn/problem/P2860|边双]]
struct Edge{
int e,n;
}e[N<<1];
int hd[N],cnt=1,n,m;
void add(int a,int b){
e[++cnt].e=b;e[cnt].n=hd[a];hd[a]=cnt;
}
int sta[N],top;
int col[N],num;
int dfn[N],low[N],tot;
int vis[N];
int ban[M<<1];
void tarjan(int p){
dfn[p]=low[p]=++tot;
int i,q;
sta[++top]=p;vis[p]=1;
for(i=hd[p];i;i=e[i].n){
q=e[i].e;
if(ban[i])continue;
if(!dfn[q]){
ban[i]=ban[i^1]=1;
tarjan(q),low[p]=min(low[p],low[q]);
}
else if(vis[q])low[p]=min(low[p],low[q]);
}
if(dfn[p]==low[p]){
num++;
while(sta[top+1]!=p){
q=sta[top];
col[q]=num;
vis[q]=0;
top--;
}
}
}
\\
===== 杂项 =====
割点和割边没有任何联系,除了他们姓一样:
割边两段不一定是割点:
{{ 2020-2021:teams:i_dont_know_png:potassium:connected_component:pic_2.png?200 }}
两割点不一定组成割边:
{{ 2020-2021:teams:i_dont_know_png:potassium:connected_component:pic_1.png?250 }}