图论
最短路
Floyd \(O(n^3)\)
/*
一般用于n < 500的图
前提条件:图权值可以为负,但不能有负权值回路,负权值回路可能不存在最短路径
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 250;
constexpr int INF = 0x3f3f3f3f;
int f[MAXN][MAXN];
void init(int n){
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
f[i][j] = INF;
}
}
for(int i = 1; i <= n; i ++) {
f[i][i] = 0;
}
}
void AddEdge(int u, int v, int w) {
f[u][v] = min(f[u][v], w);
f[v][u] = min(f[v][u], w);
}
void Floyd(){
for (int k = 1; k <= n; k ++) { // 中转点
for (int x = 1; x <= n; x ++) { // 起点
for (int y = 1; y <= n; y ++) { // 终点
f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
}
}
}
}
inline void solve() {
int n, m;
cin >> n >> m;
init(n);
for(int i = 1; i <= m; i ++) {
int a, b, c;
cin >> a >> b >> c;
AddEdge(a, b, c);
}
Floyd();
}
Johnson \(O(n \cdot m \cdot logm)\)
/*
1.设置超级源点0,向 n 个点连边权为 0 的单向边,用BellmanFord计算势能h[N]
2.通过势能调整边权
3.对每个点跑dijstra
4.还原边权
d[u][v] = f[u][v] + h[u] - h[v]
边u到v的新权值 = 边u到v的真实值 + u的势能 - v的势能
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 3e3 + 100;
constexpr ll INF = 1e9;
int n, m;
struct Edge{
int u, v;
ll w;
};
vector<Edge> edges; // 存所有的边
ll h[MAXN]; // 势能数组
bool BellmanFord(){
for(int i = 1; i <= n; i ++) h[i] = INF;
h[0] = 0ll;
for(int i = 1; i <= n; i ++) {
for(const auto &temp : edges) {
int u = temp.u, v = temp.v;
ll w = temp.w;
if(h[u] == INF) continue;
if(h[v] > h[u] + w) h[v] = h[u] + w;
}
}
for(const auto &temp : edges) {
int u = temp.u, v = temp.v;
ll w = temp.w;
if(h[u] == INF) continue;
if(h[v] > h[u] + w) return false;
}
return true;
}
struct Node{
int x;
ll w;
// 小根堆
bool operator < (const Node &u) const {
return (w == u.w) ? (x < u.x) : (w > u.w);
}
};
vector<Node> g[MAXN]; // 邻接表
ll dis[MAXN][MAXN];
void dijstra(int st){
static ll d[MAXN];
for(int i = 1; i <= n; i ++) d[i] = INF;
priority_queue<Node> pq;
pq.push({st, d[st] = 0});
bitset<MAXN> vis;
while(!pq.empty()) {
int x = pq.top().x;
pq.pop();
if(vis[x]) continue;
vis[x] = true;
for(const auto &temp : g[x]) {
int y = temp.x;
ll w = temp.w;
if(d[y] > d[x] + w) {
d[y] = d[x] + w;
pq.push({y, d[y]});
}
}
}
// 恢复权值
for(int i = 1; i <= n; i ++) {
if(d[i] == INF) dis[st][i] = d[i];
else dis[st][i] = d[i] - h[st] + h[i];
}
}
inline void solve() {
cin >> n >> m;
for(int i = 1; i <= m; i ++) {
int u, v;
ll w;
cin >> u >> v >> w;
edges.push_back({u, v, w});
g[u].push_back({v, w});
}
// 建立超级源点
for(int i = 1; i <= n; i ++) {
edges.push_back({0, i, 0ll});
}
//判负环
if(!BellmanFord()) {
cout << "-1\n";
return;
}
//得到h势能数组后, re-weight 重新设置边权
for(int u = 1; u <= n; u ++) {
for(auto &temp : g[u]){
temp.w = temp.w + h[u] - h[temp.x];
}
}
//跑 n 次 dijstra
for(int x = 1; x <= n; x ++) dijstra(x);
//输出每个点到所有点的距离之和
for(int i = 1; i <= n; i ++) {
ll ans = 0;
for(int j = 1; j <= n; j ++) ans += dis[i][j];
cout << ans << '\n';
}
}
LCA
倍增法
预处理 $O(n \cdot logn) $
查询 \(O(logn)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline void solve() {
int n;
cin >> n;
vector<vector<int>> edge(n + 1);
for(int i = 1; i < n; i ++){
int u, v;
cin >> u >> v;
edge[u].push_back(v);
edge[v].push_back(u);
}
vector<int> dep(n + 1);
constexpr int b = 20;
vector<vector<int>> fa(n + 1, vector<int>(b + 1));
function<void(int, int)> dfs = [&](int u, int pa) {
dep[u] = dep[pa] + 1;
fa[u][0] = pa;
for(int i = 1; i <= b; i ++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(const auto &v : edge[u]) {
if(v == pa) continue;
dfs(v, u);
}
};
dfs(1, 0);
function<int(int, int)> LCA = [&](int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = b; i >= 0; i --) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
for(int i = b; i >= 0; i --) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
};
int q;
cin >> q;
while(q --) {
int a, b;
cin >> a >> b;
cout << LCA(a, b) << '\n';
}
}
最大流
EK(封装)\(O(n \cdot m^2)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 250;
constexpr int INF = 0x3f3f3f3f;
struct Edge {
int from, to, cap, flow;
Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
struct EK {
int n, m; // n:点数,m:边数
vector<Edge> edges; // edges:所有边的集合
vector<int> G[MAXN]; //G:点 x -> x 的所有边在 edges 中的下标
int a[MAXN], p[MAXN]; //a:点 x -> BFS 过程中最接近点 x 的边给它的最大流
//p:点 x -> BFS 中最接近点 x 的边
void init(int n) {
this->n = n;
for(int i = 0; i < n; i ++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap) {
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
ll Maxflow(int s, int t) {
ll flow = 0;
while(1){
memset(a, 0, sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = INF;
while(!Q.empty()){
int x = Q.front();
Q.pop();
for(int i = 0; i < G[x].size(); i ++) {
Edge& e = edges[G[x][i]];
if(!a[e.to] && e.cap > e.flow) {
p[e.to] = G[x][i]; // G[x][i] 是最接近点 e.to 的边
a[e.to] = min(a[x], e.cap - e.flow);
Q.push(e.to);
}
}
if(a[t]) break; // 如果汇点接受到了流,就退出BFS
}
if(!a[t]) break; // 如果汇点没有接收到流,说明源点和汇点不在同一个连通分量上
for(int u = t; u != s; u = edges[p[u]].from) { // 通过 u 追寻 BFS 过程中 s -> t 的路径
edges[p[u]].flow += a[t]; // 增加路径上边的 flow 值
edges[p[u] ^ 1].flow -= a[t]; // 减小反向路径的 flow 值
}
flow += a[t];
}
return flow;
}
};
inline void solve() {
int n, m, s, t;
cin >> n >> m >> s >> t;
--s, --t;
EK F;
F.init(n);
for(int i = 1; i <= m; i ++) {
int a, b, c;
cin >> a >> b >> c;
--a, --b;
F.AddEdge(a, b, c);
}
ll ans = F.Maxflow(s, t);
cout << ans << '\n';
}
ISAP(封装) \(O(n^2 \cdot m)\)
/*
小心爆int!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 250;
constexpr int INF = 0x3f3f3f3f;
struct Edge {
int from, to, cap, flow;
Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};
bool operator < (const Edge& a, const Edge& b) {
return a.from < b.from || (a.from == b.from && a.to < b.to);
}
struct ISAP {
int n, m, s, t; // n:点数,m:边数,s:起点,t:终点
vector<Edge> edges; // edges:所有边的集合
vector<int> G[MAXN];
bool vis[MAXN];
int d[MAXN]; // 点在第几层
int cur[MAXN];
int p[MAXN];
int num[MAXN];
void init(int n) {
this->n = n;
for(int i = 0; i < n; i ++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap) {
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BFS() {
memset(vis, 0, sizeof(vis));
queue<int> Q;
Q.push(t);
vis[t] = true;
d[t] = 0;
while(!Q.empty()) {
int x = Q.front();
Q.pop();
for(int i = 0; i < G[x].size(); i ++) {
Edge& e = edges[G[x][i] ^ 1];
if(!vis[e.from] && e.cap > e.flow) {
vis[e.from] = true;
d[e.from] = d[x] + 1;
Q.push(e.from);
}
}
}
return vis[s];
}
int Augment() {
int x = t, a = INF;
while(x != s) {
Edge& e = edges[p[x]];
a = min(a, e.cap - e.flow);
x = edges[p[x]].from;
}
x = t;
while(x != s) {
edges[p[x]].flow += a;
edges[p[x] ^ 1].flow -= a;
x = edges[p[x]].from;
}
return a;
}
ll Maxflow(int s, int t) {
this->s = s;
this->t = t;
ll flow = 0;
BFS();
memset(num, 0, sizeof(num));
for(int i = 0; i < n; i ++) num[d[i]] ++;
int x = s;
memset(cur, 0, sizeof(cur));
while(d[s] < n) {
if(x == t) {
flow += Augment();
x = s;
}
int ok = 0;
for(int i = cur[x]; i < G[x].size(); i ++) {
Edge& e = edges[G[x][i]];
if(e.cap > e.flow && d[x] == d[e.to] + 1) {
ok = 1;
p[e.to] = G[x][i];
cur[x] = i;
x = e.to;
break;
}
}
if(!ok){
int m = n - 1;
for(int i = 0; i < G[x].size(); i ++) {
Edge& e = edges[G[x][i]];
if(e.cap > e.flow) m = min(m, d[e.to]);
}
if(--num[d[x]] == 0) break;
num[d[x] = m + 1] ++;
cur[x] = 0;
if(x != s) x = edges[p[x]].from;
}
}
return flow;
}
};
inline void solve() {
int n, m, s, t;
cin >> n >> m >> s >> t;
--s, --t;
ISAP MF;
MF.init(n);
for(int i = 1; i <= m; i ++) {
int a, b, c;
cin >> a >> b >> c;
--a, --b;
MF.AddEdge(a, b, c);
}
ll ans = MF.Maxflow(s, t);
}
Dinic(封装)
一般情况:\(O(n^2 \cdot m)\)
单位容量中:$O \left (m \cdot \min\left(m^{\frac{1}{2}}, n^{\frac{2}{3}}\right)\right) $ ;
二分图中:\(O(n^\frac{1}{2} \cdot m)\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 25000;
constexpr int INF = 0x3f3f3f3f;
struct DINIC {
struct Edge {
int v, nxt, cap, flow;
} e[MAXN];
int fir[MAXN], cnt = 0;
int n, s, t;
int d[MAXN], cur[MAXN];
void init(int n) {
this->n = n;
memset(fir, -1, sizeof(fir));
cnt = 0;
}
void AddEdge(int u, int v, int w) {
e[cnt] = {v, fir[u], w, 0};
fir[u] = cnt ++;
e[cnt] = {u, fir[v], 0, 0};
fir[v] = cnt ++;
}
bool BFS() {
queue<int> Q;
memset(d, 0, sizeof(int) * (n + 1));
d[s] = 1;
Q.push(s);
while(!Q.empty()) {
int u = Q.front();
Q.pop();
for(int i = fir[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if((!d[v]) && (e[i].cap > e[i].flow)) {
d[v] = d[u] + 1;
Q.push(v);
}
}
}
return d[t];
}
int DFS(int u, int flow) {
if((u == t) || (!flow)) return flow;
int ret = 0;
for(int& i = cur[u]; ~i; i = e[i].nxt) {
int v = e[i].v, a;
if((d[v] == d[u] + 1) && (a = DFS(v, min(flow - ret, e[i].cap - e[i].flow)))) {
ret += a;
e[i].flow += a;
e[i ^ 1].flow -= a;
if(ret == flow) return ret;
}
}
return ret;
}
ll Maxflow(int s, int t) {
ll maxflow = 0;
this->s = s;
this->t = t;
while(BFS()){
memcpy(cur, fir, sizeof(int) * (n + 1));
maxflow += DFS(s, INF);
}
return maxflow;
}
} MF;
inline void solve() {
int n, m, x;
cin >> n >> m >> s >> t;
--s, --t;
MF.init(n);
for(int i = 1; i <= m; i ++) {
int a, b, c;
cin >> a >> b >> c;
--a, --b;
MF.AddEdge(a, b, c);
}
ll ans = MF.Maxflow(s, t);
}