图论基础模板
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;
}
( ゚∀゚)o彡゜ ヒーコー ヒーコー!