联通分量全家桶
联通分量全家桶
内容有:
点双联通分量 边双联通分量 强联通分量
上述三种的缩点
基础应用: 2-SAT 圆方树
作者很憨憨,2年都没有完全分清楚这3种的区别。但好像这种题蛮多的,所以开个坑补一补。
首先说一下三种联通分量的区别:
无向图:点双和边双
有向图:强连通分量
首先是强连通分量:
在有向图上两个点\(u,v\),\(u\)可以到达\(v\),\(v\)也可以到达\(u\),则称\(u,v\)强连通。
强连通分量是有向图上的极大子图使得子图上任意两点均强联通。
常用于DP缩点,代码见2-SAT。
其次是点双联通分量:
有一个不严谨的定义:无向图上的极大子图使得子图不存在割点。 反例,只有两个点的联通子图。
常用于:仙人掌,代码见圆方树。
再就是边双联通分量:
同样不严谨的定义:无向图上地极大子图使得子图不存在桥。反例:waiting for update
常用于图论问题。
2-SAT:
stack<int>st;
int low[N<<1],vis[N<<1],dfsn[N<<1],color[N<<1],color_num,cnt,n,m;
void tarjan(int p){
dfsn[p]=++cnt;
vis[p]=1;
low[p]=dfsn[p];
st.push(p);
for(int ne=head[p];ne;ne=e[ne].Next){
int v=e[ne].v;
if(dfsn[v]){
if(vis[v])low[p]=min(low[p],dfsn[v]);
}else{
tarjan(v);
low[p]=min(low[p],low[v]);
}
}
if(low[p]==dfsn[p]){
int Top=0;
color_num++;
while(Top!=p){
Top=st.top();
st.pop();
vis[Top]=0;
color[Top]=color_num;
}
}
}//上古时期代码请不要在意码风为什么那么不一致。
上述代码为强连通分量。2-SAT既检查根据强制选择关系建立的有向图是否有一对在同一强联通分量内。
输出方案:
waiting for updating
圆方树:
void Tarjan(int p){
dfsn[p]=low[p]=++cnt;
st[++top]=p;
Rep(i,E,p){
int v=E[p][i];
if(dfsn[v]) low[p]=min(low[p],dfsn[v]); else{
Tarjan(v);
low[p]=min(low[p],low[v]);
if(low[v]==dfsn[p]){
tot++;
for(int x=0;x!=v;top--){
x=st[top];
e[tot].push_back(x);
e[x].push_back(tot);
}
e[tot].push_back(p);
e[p].push_back(tot);
}
}
}
}
这个是圆方树,求点双的时候也可以变形。值得注意的是一个点可以在很多点双里面。
边双联通分量:
求的办法实在是太多了。。。难以细说。。。
具体的可以将上述代码的\(==\)改为\(\lt\)