图论基础模板

P3388 【模板】割点(割顶)

#include <stdio.h>
#define min(x, y) ((x) < (y) ? (x) : (y))

int n, m;
int head[20003], last[200003], to[200003], ccnt = 0;
#define addedge(x, y) last[++ccnt] = head[x], to[ccnt] = y, head[x] = ccnt
int dfn[20003], low[20003], cnt = 0;
bool f[20003]; int ans = 0;

inline void dfs (int x, int fa) {
	dfn[x] = low[x] = ++cnt;
	int child = 0;
	#define y to[k]
	for (int k = head[x]; k; k = last[k]) if (!dfn[y]) {
		dfs(y, x), ++child;
		low[x] = min(low[x], low[y]);
		if (!fa && child > 1 || fa && low[y] >= dfn[x]) f[x] = 1;
	}
	else	low[x] = min(low[x], dfn[y]);
	#undef y
}

inline void kagari () {
	scanf("%d %d", &n, &m); for (int i = 1; i <= m; ++i) { int x, y; scanf("%d %d", &x, &y); addedge(x, y), addedge(y, x); }
	for (int i = 1; i <= n; ++i) low[i] = i;
	for (int i = 1; i <= n; ++i) if (!dfn[i]) dfs(i, 0);
	for (int i = 1; i <= n; ++i) if (f[i]) ++ans;
	printf("%d\n", ans); 
	for (int i = 1; i <= n; ++i) if (f[i]) printf("%d ", i);
	return;
}

int main () {
	kagari();
	return 0;
}

P8435 【模板】点双连通分量

在求割点的基础上运行. 就是对于一个连通图, 我们按照dfs的顺序将访问的节点放到一个 stack 中, 然后如果发现某个点是割点 ( 显然这是对某个儿子节点 dfs 之后发现的), 便将stack 一直弹出直到 top 是这个割点, 弹出的内容物便是一个点双连通分量.

一些定义可以看一下 https://www.luogu.com.cn/blog/zaczac/solution-p8435

#include <stdio.h>
#include <vector>
#include <stack>
#define min(x, y) ((x) < (y) ? (x) : (y))

int n, m;
int head[500003], last[4000003], to[4000003], ccnt = 0;
#define addedge(x, y) last[++ccnt] = head[x], to[ccnt] = y, head[x] = ccnt
int dfn[500003], low[500003], cnt = 0;
std:: stack < int > s;
std:: vector < int > v[1000003]; int ans = 0;

inline void dfs (int x) {
	dfn[x] = low[x] = ++cnt, s.push(x);
	#define y to[k]
	for (int k = head[x]; k; k = last[k]) if (!dfn[y]) {
		dfs(y);
		low[x] = min(low[x], low[y]); 
		if (low[y] >= dfn[x]) {
			++ans;
			while (!s.empty()) { int t = s.top(); v[ans].push_back(t), s.pop(); if (t == y) break; }
			v[ans].push_back(x);
		}
	} else low[x] = min(low[x], dfn[y]);
	#undef y
}

inline void kagari () {
	scanf("%d %d", &n, &m); for (int i = 1; i <= m; ++i) { int x, y; scanf("%d %d", &x, &y); if (x == y) continue; addedge(x, y), addedge(y, x); }
	for (int i = 1; i <= n; ++i) if (!dfn[i]) {
		if (head[i]) dfs(i);
		else v[++ans].push_back(i);
	}
	printf("%d\n", ans);
	for (int i = 1; i <= ans; ++i, puts("")) { printf("%d ", v[i].size()); for (int j: v[i]) printf("%d ", j); }
	return;
}

int main () {
	kagari();
	return 0;
}

P4782 【模板】2-SAT 问题

#include <stdio.h>
#include <stack>
#define min(x, y) ((x) < (y) ? (x) : (y))
int n, m;
int head[2000003], last[2000003], to[2000003], ccnt = 0;
#define addedge(x, y) last[++ccnt] = head[x], to[ccnt] = y, head[x] = ccnt
int dfn[2000003], low[2000003], dnt = 0; bool vis[2000003];
int color[2000003], cnt = 0;
std:: stack < int > s;

inline void dfs (int x) {
	dfn[x] = low[x] = ++dnt, vis[x] = true, s.push(x);
	for (int k = head[x]; k; k = last[k]) 
		if (!dfn[to[k]]) dfs(to[k]), low[x] = min(low[x], low[to[k]]); 
		else if (vis[to[k]]) low[x] = min(low[x], dfn[to[k]]);
	if (dfn[x] == low[x]) {
		++cnt;
		while (!s.empty() && s.top() != x) color[s.top()] = cnt, vis[s.top()] = false, s.pop();
		color[s.top()] = cnt, vis[s.top()] = false, s.pop();
	}
}

inline void kagari () {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; ++i) { int x, y, z, l; scanf("%d %d %d %d", &x, &y, &z, &l); if (x == z && y != l) continue;
		addedge(z + (l ^ 1) * n, x + y * n), addedge(x + (y ^ 1) * n, z + l * n); }
	for (int i = 1; i <= (n << 1); ++i) if (!dfn[i]) dfs(i);
	for (int i = 1; i <= n; ++i) if (color[i] == color[i + n]) { printf("IMPOSSIBLE\n"); return; }
	puts("POSSIBLE");
	for (int i = 1; i <= n; ++i) if (color[i] > color[i + n]) printf("1 "); else printf("0 ");
	return;
}

int main () {
	kagari();
	return 0;
}

P3376 【模板】网络最大流

EK 算法, 时间复杂度 \(O(nm^2)\). 每次寻找一条 S -> T 的路, 并记录这条路径的最小值.

#include <stdio.h>
#include <queue>
#define ll long long
#define min(x, y) ((x) < (y) ? (x) : (y))

int n, m, st, en;
int head[203], to[10003], last[10003], cnt = 1; ll val[10003];
#define addedge(x, y, z) to[++cnt] = y, val[cnt] = z, last[cnt] = head[x], head[x] = cnt
bool vis[203]; ll dis[203]; int frm[203];

inline bool bfs () {
	for (int i = 1; i <= n; ++i) vis[i] = false;
	std:: queue < int > q; q.push(st), dis[st] = 2147483648ll, vis[st] = true;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		#define y to[k]
		for (int k = head[x]; k; k = last[k]) if (val[k] != 0 && !vis[y]) {
			vis[y] = true, dis[y] = min(dis[x], val[k]), frm[y] = k, q.push(y);
			if (y == en) return true;
		}
		#undef y
	}
	return false;
}

ll ans = 0;
inline void update () {
	ans += dis[en];
	int t = en; while (t != st) val[frm[t]] -= dis[en], val[frm[t] ^ 1] += dis[en], t = to[frm[t] ^ 1];
}

inline void kagari () {
	scanf("%d %d %d %d", &n, &m, &st, &en);
	for (int i = 1; i <= m; ++i) { int x, y; ll z; scanf("%d %d %lld", &x, &y, &z); addedge(x, y, z), addedge(y, x, 0); }
	while (bfs()) update();
	printf("%lld\n", ans);
	return;
}

int main () {
	kagari();
	return 0;
}

dinic 算法, 先进行 bfs, 计算每个点与源点的距离 \(dep_i\). 我们令节点 \(x\) 只能前往节点 \(y\), 当且仅当 \(dep_y=dep_x+1\). 时间复杂度 \(O(n^2m)\)

#include <stdio.h>
#include <queue>
#define ll long long
inline int min(int x, int y) { return ((x) < (y) ? (x) : (y)); }
inline ll min(ll x, ll y) { return ((x) < (y) ? (x) : (y)); }

int n, m, st, en;
int head[203], to[10003], last[10003], cnt = 1; ll val[10003];
#define addedge(x, y, z) to[++cnt] = y, val[cnt] = z, last[cnt] = head[x], head[x] = cnt
int dep[203], frm[203], nh[203];
ll ans = 0;

inline bool bfs () {
	for (int i = 1; i <= n; ++i) dep[i] = 1000;
	std:: queue < int > q; q.push(st), dep[st] = 1, nh[st] = head[st];
	while (!q.empty()) {
		int x = q.front(); q.pop();
		#define y to[k]
		for (int k = head[x]; k; k = last[k]) if (val[k] > 0 && dep[y] == 1000) {
			dep[y] = dep[x] + 1, nh[y] = head[y], q.push(y);
			if (y == en) return true;
		}
		#undef y
	}
	return false;
}

inline ll dfs (int x, ll sum) {
	if (x == en) return sum;
	ll cum = 0ll;
	#define y to[k]
	for (int k = nh[x]; k && sum; k = last[k]) {
		nh[x] = k; 
		if (val[k] > 0 && dep[y] == dep[x] + 1) {
			ll cost = dfs(y, min(sum, val[k]));
			if (cost == 0) dep[y] = 1000;	// 剪枝 ( 从 y 流不到汇点 )
			val[k] -= cost, val[k ^ 1] += cost, sum -= cost, cum += cost;
		}
	}
	#undef y
	return cum;
}

inline void kagari () {
	scanf("%d %d %d %d", &n, &m, &st, &en);
	for (int i = 1; i <= m; ++i) { int x, y; ll z; scanf("%d %d %lld", &x, &y, &z); addedge(x, y, z), addedge(y, x, 0); }
	ll ans = 0ll;
	while (bfs()) ans += dfs(st, 2147483648ll);
	printf("%lld\n", ans);
	return;
}

int main () {
	kagari();
	return 0;
}
posted @ 2023-03-23 21:24  dbg_8  阅读(26)  评论(0编辑  收藏  举报