首页 > 代码库 > 【图论】双连通总结

【图论】双连通总结

双连通总结

这类问题分为,边-双连通。点-双连通

边双连通

边双连通,求出来后。连接没一个双连通的分量的就是割边,因此能够缩点成一棵树。把问题转化为在树上搞,割边的定义为:去掉这条边后图将不连通

基本这类题都一个解法。求双连通分量,然后缩点成树,进行操作
或者就是直接要求割边,做跟割边相关的操作

模板:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 10005;
const int M = 20005;

int n, m, val[N];

struct Edge {
	int u, v, id;
	bool iscut;
	Edge() {}
	Edge(int u, int v, int id) {
		this->u = u;
		this->v = v;
		this->id = id;
		this->iscut = false;
	}
} edge[M * 2], cut[M];

int en, first[N], next[M], cutn;

void init() {
	memset(first, -1, sizeof(first));
	en = 0;
}

void add_edge(int u, int v, int id) {
	edge[en] = Edge(u, v, id);
	next[en] = first[u];
	first[u] = en++;
}

int pre[N], dfn[N], bccno[N], bccn, dfs_clock;

void dfs_cut(int u, int fa) {
	pre[u] = dfn[u] = ++dfs_clock;
	for (int i = first[u]; i + 1; i = next[i]) {
		int v = edge[i].v;
		if (edge[i].id == fa) continue;
		if (!pre[v]) {
			dfs_cut(v, edge[i].id);
			dfn[u] = min(dfn[u], dfn[v]);
			if (dfn[v] > pre[u]) {
				edge[i].iscut = edge[i^1].iscut = true;//标记割边
				cut[cutn++] = edge[i];//存放割边
			}
		} else dfn[u] = min(dfn[u], pre[v]);
	}
}

void find_cut() {
	dfs_clock = cutn = 0;
	memset(pre, 0, sizeof(pre));
	for (int i = 0; i < n; i++)
		if (!pre[i]) dfs_cut(i, -1);
}

void dfs_bcc(int u) {
	pre[u] = 1;
	bccno[u] = bccn;
	for (int i = first[u]; i + 1; i = next[i]) {
		if (edge[i].iscut) continue;
		int v = edge[i].v;
		if (pre[v]) continue;
		dfs_bcc(v);
	}
}

vector<int> bcc[N];

void find_bcc() {
	bccn = 0;
	memset(pre, 0, sizeof(pre));
	for (int i = 0; i < n; i++) {
		if (!pre[i]) {
			dfs_bcc(i);
			bccn++;
		}
	}
}

int main() {

	return 0;
}

HDU 2242 考研路茫茫——空调教室 边双连通缩点+dfs
HDU 2460 Network 边双连通缩点+树链剖分
HDU 3849By Recognizing These Guys, We Find Social Networks Useful 求桥
HDU 4005 The war 边双连通缩点+dfs
HDU 3394 Railway 双连通求块
3177 Redundant Paths 构造边双连通,利用度数去求
3352 Road Construction 构造边双连通,利用度数去求
1515 Street Directions 无向图改有向图,双连通内部肯定都能改,桥不能改
1438 One-way Traffic 混合图改有向图(这题的数据貌似有点问题)

点双连通

点双连通,求出来个,连接的是割点,注意一个割点可能属于不同的点-双连通分量。割点定义为:去掉这个点后,图将不连通

模板:

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
using namespace std;

const int N = 1005;
const int M = 2000005;
int n, m;

struct Edge {
	int u, v;
	Edge() {}
	Edge(int u, int v) {
		this->u = u;
		this->v = v;
	}
} edge[M];

int first[N], next[M], en;

void add_edge(int u, int v) {
	edge[en] = Edge(u, v);
	next[en] = first[u];
	first[u] = en++;
}

void init() {
	en = 0;
	memset(first, -1, sizeof(first));
}

int pre[N], dfn[N], bccno[N], dfs_clock, bccn;
bool iscut[N];

stack<Edge> S;
vector<int> bcc[N];

void dfs_bcc(int u, int fa) {
	dfn[u] = pre[u] = ++dfs_clock;
	int child = 0;
	for (int i = first[u]; i + 1; i = next[i]) {
		int v = edge[i].v;
		if (fa == v) continue;
		if (!pre[v]) {
			S.push(edge[i]);
			child++;
			dfs_bcc(v, u);
			dfn[u] = min(dfn[u], dfn[v]);
			if (dfn[v] >= pre[u]) {
				iscut[u] = true;
				bccn++;
				bcc[bccn].clear();
				while (1) {
					Edge x = S.top(); S.pop();
					if (bccno[x.u] != bccn) {bcc[bccn].push_back(x.u); bccno[x.u] = bccn;}
					if (bccno[x.v] != bccn) {bcc[bccn].push_back(x.v); bccno[x.v] = bccn;}
					if (x.u == u && x.v == v) break;
				}
			}
		} else if (pre[v] < pre[u] && v != fa) {
			S.push(edge[i]);
			dfn[u] = min(dfn[u], pre[v]);
		}
	}
	if (fa == 0 && child == 1) iscut[u] = 0;
}

void find_bcc() {
	memset(pre, 0, sizeof(pre));
	memset(bccno, 0, sizeof(bccno));
	memset(iscut, false, sizeof(iscut));
	dfs_clock = bccn = 0;
	for (int i = 1; i <= n; i++)
		if (!pre[i]) dfs_bcc(i, 0);
}


POJ 2942 Knights of the Round Table 点双连通经典题,利用点双连通和二染色去做
UVA 1108 Mining Your Own Business 点双连通分量+计数

【图论】双连通总结