点双连通分量,边双连通分量
边双连通
1.边双连通的概念:对于任意两个点u,v,如果删除任意一条边都不能使得u,v之间不连通,那么u和v之间就称为边双连通。
2.边双连通分量的求法:其实一个双连通分量就是若干个点组成的强连通分量然后被一条割边堵住了而已,所以就和求Scc的方法类似,还是tarjan深搜找割边,然后不能走返祖边,然后记录每条边的编号还有一个技巧,就是第i条边的正向边记为i << 1,反向边记为i << 1 | 1。
3.边双缩点之后我们可以发现原来的图变成了一颗无向树,然后所有树边都是原来的割边。
4.代码如下:
struct Edcc {
int tot, cnt, n;
vi stk, instk;
vi dfn, low, X;
vvi edcc;
vvpi g;
Edcc(){};
Edcc(int s) {
n = s, cnt = 0, tot = 0;
instk.resize(s + 1);
dfn.resize(s + 1);
low.resize(s + 1);
X.resize(s + 1);
g.assign(s + 1, vpi());
edcc.assign(s + 1, vi());
}
void add(int u, int v, int id) {
g[u].push_back({v, id});
}
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++tot;
instk[u] = 1, stk.push_back(u);
for(auto [v, id] : g[u]) {
if(id == (fa ^ 1)) continue;
if(!dfn[v]) {
tarjan(v, id);
low[u] = min(low[u], low[v]);
} else if(instk[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if(dfn[u] == low[u]) {
int y;
cnt++;
do {
y = stk.back();
stk.pop_back();
instk[y] = 0, X[y] = cnt;
edcc[cnt].push_back(y);
} while(y != u);
}
}
void work() {
for(int i = 1; i <= n; i++) {
if(!dfn[i]) tarjan(i, 0);
}
}
};
4.例题:
- 洛谷P2860
<1>题目大意:给一个无向图,问最少加多少条边让整张图变成一个边双图。
<2>做法:直接求边双缩点,然后找树上的叶子结点的个数,ans = (leaf + 1)/2,因为我们发现要加最少的边的话,那么我们肯定是给叶子结点两两连边。 - CF1000E
<1>题目大意:在一张连通无向图中找到两个点u,v,使得u和v之前存在一条路径上的割边数最多。
<2>做法:直接边双缩点然后求个直径就可以了。
点双连通
1.点双连通的概念:若一张无向图不存在割点,那么这张图就称为“点双连通图”,无向图的极大点双连通子图被称为“点双连通分量”。
2.做法:还是用tarjan算法找割点,找到割点之后直接让栈内元素出栈就可以了,最后再让割点入栈。还要特判一下,如果是一个孤立的结点,也算一个点双连通分量。
3.代码如下:
struct Vdcc {
int tot, cnt, n;
vi stk;
vi dfn, low, X, cut;
vvi vdcc;
vvi g;
Vdcc(){};
Vdcc(int s) {
n = s, cnt = 0, tot = 0;
dfn.resize(s + 1);
low.resize(s + 1);
X.resize(s + 1);
cut.resize(s + 1);
g.assign(s + 1, vi());
vdcc.assign(s*2 + 1, vi());
}
void add(int u, int v) {
g[u].push_back(v);
}
void tarjan(int u, int root) {
dfn[u] = low[u] = ++tot;
stk.push_back(u);
int child = 0;
for(auto v : g[u]) {
if(!dfn[v]) {
tarjan(v, root);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
child++, cnt++;
if(u != root || child > 1) cut[u] = 1;
int y;
do {
y = stk.back();
stk.pop_back();
vdcc[cnt].push_back(y);
X[y] = cnt;
} while(y != v);
vdcc[cnt].push_back(u);
}
} else low[u] = min(low[u], dfn[v]);
}
if(u == root && child == 0 || g[u].size() == 0) {
vdcc[++cnt].push_back(u);
}
}
void work() {
for(int i = 1; i <= n; i++) {
if(!dfn[i]) tarjan(i, i);
}
}
};