图论模板(部分)

图论模板(部分)

maincpp

#include <iostream>
#include <climits>
#include <limits>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);return 0;
}

最短路

Dijkstra:

struct Node {int y, v; // 连到哪里 边权Node(int _y, int _v) {y = _y; v = _v;};
};
int N; // 一共有多少个点
int n, m; // 多少个顶点 多少条边
std::vector<int> dist(N + 1, MAX_INT); // 每个点最短路径长度
std::vector<std::vector<Node>> edge(N + 1); // 1 ~ N
std::vector<bool> C(N + 1, false); // inline int dijkstra(int s, int t) { // 起点 终点std::fill(C.begin(), C.end(), false);std::fill(dist.begin(), dist.end(), MAX_INT);dist[s] = 0; // 初始化起点while(1) {int x = -1; // x 是不在C(点集)里面,并且离起点最近的那个点的下标for(int i = 1; i <= n; i++) { // 遍历n个点if(!C[i] && dist[i] < 1 << 30) { // 如果点集C里面没有这个点,并且能达到if(x == -1 || dist[i] < dist[x]) {x = i;}}}if(x == t || x == -1) break; // 如果说x到达了终点t或者没有任何一个能达到的点就直接结束C[x] = true; // 加入到点集Cfor(const auto& i : edge[x]) dist[i.y] = std::min(dist[i.y], dist[x] + i.v); // 更新dist最小的点能达到的边的值}return dist[t];}

Dijkstra (堆优化):

std::vector<std::vector<PII>> e;
std::vector<int> dist;
std::vector<bool> vis;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q;inline void Dijkstra() {q.push({0, s});dist[s] = 0;while(!q.empty()) {auto [val, u] = q.top();q.pop();if(vis[u]) continue;vis[u] = true;for(const auto&[v, w] : e[u]) {if(dist[v] > dist[u] + w) {dist[v] = dist[u] + w;q.push({dist[v], v});}}}}

SPFA:

struct Edge {int x, y, v;
};
int n, m, k;
std::vector<int> dist;
std::vector<Edge> edge;
std::vector<int> vis;  // 标记节点是否在队列中
std::vector<int> cnt;      // 记录每个节点入队的次数,用于判断负环inline void SPFA(int s, int t) {// 初始化距离数组std::fill(dist.begin(), dist.end(), INT_MAX);dist[s] = 0;// 初始化标记数组和入队次数数组std::fill(vis.begin(), vis.end(), false);std::fill(cnt.begin(), cnt.end(), 0);std::queue<int> q;q.push(s);vis[s] = true;cnt[s]++;while (!q.empty()) {int u = q.front();q.pop();vis[u] = 0;rep(i, m) {if (edge[i].x == u) {int v = edge[i].y;int w = edge[i].v;if (dist[u] + w < dist[v]) {dist[v] = dist[u] + w;if (!vis[v]) {q.push(v);vis[v] = true;cnt[v]++;// 如果某个节点入队次数超过 n 次,说明存在负环if (cnt[v] > n) {std::cout << "-1\n";return;}}}}}}// 输出结果if (dist[t] < INT_MAX) std::cout << dist[t] << '\n';else std::cout << "-1\n";
}

拓扑排序:

int n;
std::vector<std::vector<int>> e;
std::vector<int> d;
std::queue<int> q;inline void TopoSort(int x) {Rep(i, 1, n + 1) if(!d[i]) q.push(x);while(!q.empty()) {int u = q.front();q.pop();std::cout << u << '\n';for(const int& v : e[u]) {if(--d[v] == 0) {q.push(v);}}}
}

Floyd:

int n;
std::vector<std::vector<int>> f, e;
std::vector<int> d;
std::queue<int> q;inline void Floyd() {f.resize(n + 1, std::vector<int>(n + 1, INF));f = e;Rep(i, 1, n + 1) f[i][i] = 0;Rep(k, 1, n + 1) {Rep(i, 1, n + 1) {Rep(j, 1, n + 1) {if(f[i][k] < INF && f[k][j] < INF) {f[i][j] = std::min(f[i][j], f[i][k] + f[k][j]);}}}}}

传递闭包(Floyd):

 inline void Floyd() {Rep(k, 1, n + 1) {Rep(i, 1, n + 1) {Rep(j, 1, n + 1) {e[i][j] |= (e[i][k] & e[k][j]);}}}}

Floyd(bitset优化):

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <bitset>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();int n;
std::vector<std::bitset<110>> e;inline void Floyd() {Rep(j, 1, n + 1) Rep(i, 1, n + 1) if(e[i][j]) e[i] |= e[j];
}int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);std::cin >> n;e.resize(n + 1);Rep(i, 1, n + 1) {Rep(j, 1, n + 1) {int val;std::cin >> val;e[i][j] = val;}}Floyd();Rep(i, 1, n + 1) {Rep(j, 1, n + 1) std::cout << e[i][j] << " ";std::cout << '\n';}return 0;
}

Floyd求无向图最小环:

int MinCycle(int n) {int res = INF;vector<vector<int>> d(n + 1, vector<int>(n + 1, INF));Rep(i, 1, n + 1) d[i][i] = 0;Rep(i, 1, m + 1) {int u, v, w;cin >> u >> v >> w;d[u][v] = d[v][u] = min(d[u][v], w); // 处理重边}Rep(k, 1, n + 1) {rep(i, k) Rep(j, i + 1, k) res = min(res, d[i][j] + d[i][k] + d[k][j]);Rep(i, 1, n + 1) Rep(j, 1, n + 1) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);}return res == INF ? -1 : res; // 无环返回-1
}

差分约束:

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <queue>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();int n, m;
std::vector<std::vector<PII>> e;
std::vector<int> dist, cnt;
std::vector<bool> vis;
std::queue<int> q;inline void SPFA() {q.push(0);dist[0] = 0;vis[0] = true;cnt[0]++;while(!q.empty()) {int u = q.front();q.pop();vis[u] = false;for(const auto&[v, w] : e[u]) {if(dist[v] > dist[u] + w) {dist[v] = dist[u] + w;if(!vis[v]) {q.push(v);vis[v] = true;cnt[v]++;if(cnt[v] > n) {std::cout << "NO\n";exit(0);}}}}}
}int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);std::cin >> n >> m;dist.resize(n + 1, INF);e.resize(n + 1);vis.resize(n + 1, false);cnt.resize(n + 1, 0);Rep(i, 1, n + 1) e[0].push_back({i, 0});rep(i, m) {int a, b, c;std::cin >> a >> b >> c;e[b].push_back({a, c});}SPFA();Rep(i, 1, n + 1) std::cout << dist[i] << " ";return 0;
}

prim:

int N, M, res = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> dist;
std::vector<bool> vis;inline void prim() {dist[1] = 0;rep(i, N) {int t = -1;Rep(j, 1, N + 1) if (!vis[j] && (t == -1 || dist[j] < dist[t])) t = j;if (t == -1 || dist[t] == INF) break;vis[t] = true;res += dist[t];cnt++;Rep(j, 1, N + 1) if (!vis[j]) dist[j] = std::min(dist[j], e[t][j]);}
}

Kruskal:

struct edge {ll u, v, w;bool operator< (const edge& other) const{return w < other.w;}
};ll N, K, res = 0;
std::vector<edge> e;
std::vector<ll> dist, fa;
std::unordered_map<ll, bool> mp;ll Findest(ll x) {return x == fa[x] ? x : fa[x] = Findest(fa[x]);
}inline void Kruskal() {std::sort(e.begin(), e.end());Rep(i, 1, N + 1) fa[i] = i;rep(i, e.size()) {ll u = e[i].u, v = e[i].v, w = e[i].w;ll fu = Findest(u), fv = Findest(v);if(fu == fv) continue;res += w;fa[fu] = fv;}}

LCA(倍增):

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <cmath>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();int N, M, S, LOG;
std::vector<std::vector<int>> e, fa;
std::vector<int> dep;void DFS(int u, int father) {dep[u] = dep[father] + 1;fa[u][0] = father;for(int i = 1; i < LOG; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];for(const int& v : e[u]) if(v != father) DFS(v, u);
}inline int LCA(int u, int v) {if(dep[u] < dep[v]) std::swap(u, v);for(int i = LOG - 1; i >= 0; i--) if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];if(u == v) return v;for(int i = LOG - 1; i >= 0; i--) if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i]; return fa[u][0];
}int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);std::cin >> N >> M >> S;LOG = log2(N) + 1;e.resize(N + 1);fa.resize(N + 1, std::vector<int>(LOG, 0));dep.resize(N + 1, 0);rep(i, N - 1) {int x, y;std::cin >> x >> y;e[x].push_back(y);e[y].push_back(x); }dep[S] = 0;DFS(S, 0);while(M--) {int x, y;std::cin >> x >> y;std::cout << LCA(x, y) << '\n';}return 0;
}

LCA(树链剖分):

void DFS1(int u, int father) {dep[u] = dep[father] + 1, sz[u] = 1, fa[u] = father;for(const int& v : e[u]) {if(v == father) continue;DFS1(v, u);sz[u] += sz[v];if(sz[son[u]] < sz[v]) son[u] = v;  }
}void DFS2(int u, int t) {top[u] = t;if(!son[u]) return ;DFS2(son[u], t);for(const int& v : e[u]) if(v != fa[u] && v != son[u]) DFS2(v, v);
}inline int LCA(int u, int v) {while(top[u] != top[v]) {if(dep[top[u]] < dep[top[v]]) std::swap(u, v);u = fa[top[u]];}return dep[u] < dep[v] ? u : v;
}

Kruskal重构树:

构建最大生成树,建立起来的父节点都是两点之间最小边权的最大值
构建最小生成树,建立起来的父节点都是两点之间最大边权的最小值

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <algorithm>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();struct edge {int u, v, w;bool operator> (const edge& other) const{return w > other.w;}
};int n, m, q;
std::vector<edge> adj;
std::vector<std::vector<int>> e;
std::vector<int> dist, sz, dep, son, top, fa, F, cost;inline void Init() {std::cin >> n >> m;sz.resize(2 * n + 1, 0);dep.resize(2 * n + 1, 0);fa.resize(2 * n + 1, 0);F.resize(2 * n + 1, 0);son.resize(2 * n + 1, 0);cost.resize(2 * n + 1, -1);top.resize(2 * n + 1, 0);e.resize(2 * n + 1);}void DFS1(int u, int father) {dep[u] = dep[father] + 1, sz[u] = 1, fa[u] = father;for(const int& v : e[u]) {if(v == father) continue;DFS1(v, u);sz[u] += sz[v];if(sz[son[u]] < sz[v]) son[u] = v;  }
}void DFS2(int u, int t) {top[u] = t;if(!son[u]) return ;DFS2(son[u], t);for(const int& v : e[u]) if(v != fa[u] && v != son[u]) DFS2(v, v);
}inline int LCA(int u, int v) {while(top[u] != top[v]) {if(dep[top[u]] < dep[top[v]]) std::swap(u, v);u = fa[top[u]];}return dep[u] < dep[v] ? u : v;
}int Findest(int x) {return x == F[x] ? x : F[x] = Findest(F[x]);
}inline void KruskalBuildTree() {std::sort(adj.begin(), adj.end(), std::greater<edge>());Rep(i, 1, n + 1) F[i] = i;int tot = n;rep(i, adj.size()) {int u = adj[i].u, v = adj[i].v, w = adj[i].w;int fu = Findest(u), fv = Findest(v);if(fu == fv) continue;cost[++tot] = w;F[tot] = F[fu] = F[fv] = tot;e[tot].push_back(fu);e[tot].push_back(fv);e[fu].push_back(tot);e[fv].push_back(tot);}Rep(i, 1, tot + 1) if(F[i] == i) DFS1(i, 0), DFS2(i, i);
}int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);Init();rep(i, m) {int a, b, c;std::cin >> a >> b >> c;adj.push_back({a, b, c});}KruskalBuildTree();std::cin >> q;while(q--) {int x, y;std::cin >> x >> y;std::cout << cost[LCA(x, y)] << '\n';}return 0;
}

Tarjan(scc):

int n, m, tot = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> low, dfn, id, sz;
std::vector<bool> in_stk;
std::stack<int> stk;void Tarjan(int u) {low[u] = dfn[u] = ++tot;stk.push(u), in_stk[u] = true;for(const int& v : e[u]) {if(!dfn[v]) {Tarjan(v);low[u] = std::min(low[u], low[v]);}else if(in_stk[v]) low[u] = std::min(low[u], dfn[v]);}if(dfn[u] == low[u]) {int v;cnt++;do{v = stk.top();stk.pop();in_stk[v] = false;id[v] = cnt;sz[cnt]++;}while(v != u);}
}

Tarjan(scc割边):

void Tarjan(int u, int father) {low[u] = dfn[u] = ++tot;for(const int& v : e[u]) {if(v == father) continue;if(!dfn[v]) {Tarjan(v, u);low[u] = std::min(low[u], low[v]);if(low[v] > dfn[u]) output.push_back({u, v});}else low[u] = std::min(low[u], dfn[v]);}
}

Tarjan(scc割点):

inline void Tarjan(int u) {low[u] = dfn[u] = ++cnt;int child = 0;for(const int& v : e[u]) {if(!dfn[v]) {Tarjan(v);low[u] = std::min(low[u], low[v]);if(low[v] >= dfn[u]) {child++;if(child > 1 || u != root) cut[u] = true;}}else low[u] = std::min(low[u], dfn[v]);}
}

Tarjan(edcc):

int n, m, tot = 0, cnt = 0;
std::vector<edge> e;
std::vector<int> low, dfn, h, bri, dcc, d ;
std::stack<int> stk;inline void add(int a, int b) {e.push_back({b, h[a]});h[a] = e.size() - 1;
}void Tarjan(int u, int in_edge) {low[u] = dfn[u] = ++tot;stk.push(u);for(int i = h[u]; ~i; i = e[i].ne) {int v = e[i].v;if(!dfn[v]) {Tarjan(v, i);low[u] = std::min(low[u], low[v]);if(low[v] > dfn[u]) bri[i] = bri[i ^ 1] = true;}else if(i != (in_edge ^ 1)) low[u] = std::min(low[u], dfn[v]);}if(dfn[u] == low[u]) {int v;cnt++;do{v = stk.top();stk.pop();dcc[v] = cnt;}while(v != u);}
}

Tarjan(vdcc):

int n, m, tot = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> low, dfn, dcc;
std::stack<int> stk;inline void Tarjan(int u) {low[u] = dfn[u] = ++cnt;if(e[u].empty()) {dcc[++cnt].push_back(u);return ;}int child = 0;for(const int& v : e[u]) {if(!dfn[v]) {Tarjan(v);low[u] = std::min(low[u], low[v]);if(low[v] >= dfn[u]) {child++;if(child > 1 || u != root) cut[u] = true;int ne;cnt++;do{ne = stk.top();stk.pop();dcc[cnt].push_back(ne);}while(ne != u);}}else low[u] = std::min(low[u], dfn[v]);}
}main(建图)//给每个割点一个新编号(cnt+1开始)
num = cnt;
Rep(i, 1, n + 1) if(cut[i]) id[i] = ++num;
//建新图,从每个vDCC向对应割点连边
Rep(u, 1, cnt + 1){for(const int& v : e[u]){if(cut[v]){ne[i].push_back(id[v]);ne[id[v]].push_back(i);}}
}

2-SAT:

int n, m, tot = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> low, dfn, id, sz;
std::vector<bool> in_stk;
std::stack<int> stk;void Tarjan(int u) {low[u] = dfn[u] = ++tot;stk.push(u), in_stk[u] = true;for(const int& v : e[u]) {if(!dfn[v]) {Tarjan(v);low[u] = std::min(low[u], low[v]);} else if(in_stk[v]) low[u] = std::min(low[u], dfn[v]);}if(dfn[u] == low[u]) {int v;cnt++;do{v = stk.top();stk.pop();in_stk[v] = false;id[v] = cnt;  // 记录SCC编号sz[cnt]++;}while(v != u);}
}// 添加约束:x取a或y取b(a,b∈{0,1})
// 注意:变量x的编号应为0到n-1
void add_clause(int x, int a, int y, int b) {// 变量x的真值为2x,假值为2x+1int u = 2 * x + a;int v = 2 * y + b;e[u ^ 1].push_back(v);   // ¬u → ve[v ^ 1].push_back(u);   // ¬v → u
}bool solve_2sat() {// 初始化数组dfn.resize(2 * n + 2, 0);low.resize(2 * n + 2, 0);id.resize(2 * n + 2, 0);sz.resize(2 * n + 2, 0);in_stk.resize(2 * n + 2, false);e.resize(2 * n + 2);rep(i, 2 * n) if (!dfn[i]) Tarjan(i);// 检查每个变量的两个状态是否在同一SCC中rep(i, n) if (id[2 * i] == id[2 * i+1]) return false;return true;
}

欧拉回路(有向图):

1.对于无向图
(1)存在欧拉路径的充分必要条件:度数为奇数的点只能有0或2个。
(2)存在欧拉回路的充分必要条件:度数为奇数的点只能有0个。2.对于有向图,所有边都是连通。
(1)存在欧拉路径的充分必要条件:要么所有点的出度均等于入度;要么除了两个
点之外,其余所有点的出度等于入度,剩余的两个点:一个满足出度比入度多1(起
点),另一个满足入度比出度多1(终点)。
(2)存在欧拉回路的充分必要条件:所有点的出度均等于入度。
#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <algorithm>
#include <stack>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();int n, m, s = 1;
std::vector<std::vector<int>> e;
std::vector<int> din, dout, del;
std::stack<int> stk;void DFS(int u) {for(int i = del[u]; i < e[u].size(); i = del[u]) {del[u] = i + 1;DFS(e[u][i]);}stk.push(u);
}int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);std::cin >> n >> m;e.resize(n + 1);din.resize(n + 1);dout.resize(n + 1);del.resize(n + 1, 0);rep(i, m) {int a, b;std::cin >> a >> b;e[a].push_back(b);din[b]++, dout[a]++;}Rep(i, 1, n + 1) std::sort(e[i].begin(), e[i].end());int cnt_out = 0, cnt_in = 0;bool flag = false;Rep(i, 1, n + 1) {if(din[i] != dout[i]) { // 有入度出度不相等的点 flag = true;int delta = dout[i] - din[i]; // 出度 - 入度if(delta == 1) cnt_out++, s = i; // 起点else if(delta == -1) cnt_in++;else {std::cout << "No\n";return 0;}}}if(flag && cnt_in != cnt_out && cnt_out != 1) {std::cout << "No\n";return 0;}DFS(s);while(!stk.empty()) std::cout << stk.top() << " ", stk.pop();return 0;
}

欧拉回路(无向图):

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <stack>
#include <unordered_map>
#include <algorithm>typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000const int INF = std::numeric_limits<int>::max();int m;
std::vector<std::vector<int>> e;
//std::vector<std::vector<bool>> vis;
std::vector<int> odd, d;
std::unordered_map<int, std::unordered_map<int, int>> mp;
std::stack<int> stk;void DFS(int u) {/*// 如果两点间只能一条路径经过一次while(!e[u].empty()) {int v = e[u].back();e[u].pop_back();if(vis[u][v] || vis[v][u]) continue;vis[u][v] = vis[v][u] = true;DFS(v);}*/// 如果两点有多条路径,并且每条路径都能经过一次for(const int& v : e[u]) {if(mp[u][v]) {mp[u][v]--;mp[v][u]--;DFS(v);}}stk.push(u);
}int main(void) {std::ios::sync_with_stdio(false);std::cin.tie(nullptr), std::cout.tie(nullptr);std::cin >> m;e.resize(500 + 1);d.resize(500 + 1);//vis.resize(500 + 1, std::vector<bool>(500 + 1, false));//mp.resize(500 + 1, std::vector<int>(500 + 1, 0));rep(i, m) {int a, b;std::cin >> a >> b;e[a].push_back(b);e[b].push_back(a);mp[a][b]++, mp[b][a]++;d[a]++, d[b]++;}Rep(i, 1, 500 + 1) if(!e[i].empty()) std::sort(e[i].begin(), e[i].end());Rep(i, 1, 500 + 1) if(!e[i].empty() && d[i] & 1) odd.push_back(i); // 不是孤点,并且是奇数int s = INF;if(odd.size() == 2) s = std::min(odd[0], odd[1]);else {Rep(i, 1, 500 + 1) {if(d[i]) {s = i;break;}}}if(s == INF) s = 1;DFS(s);while(!stk.empty()) std::cout << stk.top() << '\n', stk.pop();return 0;
}

树的直径:

int max_v = 0;
std::vector<std::vector<PII>> e;
std::vector<int> dist, from, a;void DFS(int u, int fa) {from[u] = fa; // 记录直径路径上的点for(const auto& [v, w] : e[u]) {if(v == fa) continue;dist[v] = dist[u] + w; // 记录距离if(dist[v] > dist[max_v]) max_v = v; // 更新于起点最远的点DFS(v, u);}
}inline void Build(int u) {while(u) { // 获取路径(反向)a。push_back(u);u = from[u];}std::reverse(a.begin(), a.end()); // 变成从起点到终点
}inline void TreeL() {DFS(1, 0);dist[max_v] = 0;DFS(max_v, 0);Build(max_v);
}

树的重心:

树的中心:
树中所有节点的最长最短路径的中点(即树的直径的中点)
应用场景:选择一个点,使得最远节点的距离最小(如紧急集合点问题)。树的重心:
树中所有节点到该点的距离之和最小的点
应用场景:选择一个点,使得所有节点的距离总和最小
int n;
std::vector<std::vector<int>> e;
std::vector<int> sz, dist, d;
std::vector<bool> vis;int DFS(int u){vis[u] = true; //标记u这个点被搜过int sz = 0; //记录u的最大子树的结点数int sum = 1;  //记录以u为根的子树的结点数for(const int& v : e[u]){ if(vis[v]) continue; //避免向上查找int s = DFS(v); //s是以v为根的子树的结点数sz = std::max(sz,s); //记录u的最大子树的结点数sum += s; //累加u的各个子树的结点数}ans = min(ans, max(sz, n - sum)); //去掉重心每个区间最大的节点数的最小值return sum; //返回以u为根的子树的结点数
}

树的中心:

 int n, sum = 0;std::vector<std::vector<int>> e;std::vector<int> d1, d2, up, p;std::vector<bool> vis;// 向下找找到最长路径和次长路径int DFSDown(int u, int fa) {d1[u] = d2[u] = 0;for(const int& v : e[u]) {if(v == fa) continue;int d = DFSDown(v, u) + 1;if(d >= d1[u]) {d2[u] = d1[u];d1[u] = d;p[u] = v; // 记录u的子节点是谁}else if(d > d2[u]) d2[u] = d;}return d1[u];}// 向上查找,如果说 v 位于最长路,则比较次长路和向上走那个距离更远,更新up[v]// 如果说 v 位于次长路,则比较最长路和向上走那个距离更远,更新up[v]void DFSUp(int u, int fa) {for(const int& v : e[u]) {if(v == fa) continue;if(p[u] == v) up[v] = std::max(up[u], d2[u]) + 1;else up[v] = std::max(up[u], d1[u]) + 1;DFSUp(v, u);}}main(输出树的中心)DFSDown(任意点x, -1);
DFSUp(任意点x, -1);int res = INF;
Rep(i, 1, n + 1) res = std::min(res, std::max(up[i], d1[i]));
std::cout << res << '\n';

树上操作整合:

const int INF = std::numeric_limits<int>::max();int n, sum = 0, max_v = 0, ans = INF;
std::vector<std::vector<PII>> e;
std::vector<int> sz, dist, d, d1, d2, up, p, from, a;
std::vector<bool> vis;// 树的直径
void DFS(int u, int fa) {from[u] = fa; // 记录直径路径上的点for (const auto& [v, w] : e[u]) {if (v == fa) continue;dist[v] = dist[u] + w; // 记录距离if (dist[v] > dist[max_v]) max_v = v; // 更新于起点最远的点DFS(v, u);}
}// 树的重心(重载)
int DFS(int u) {vis[u] = true; // 标记u这个点被搜过sz[u] = 1; // 初始化以u为根的子树的结点数int max_sz = 0;  // 记录u的最大子树的结点数for (const auto& [v, w] : e[u]) {if (vis[v]) continue; // 避免向上查找int s = DFS(v); // s是以v为根的子树的结点数sz[u] += s; // 累加u的各个子树的结点数max_sz = std::max(max_sz, s); // 记录u的最大子树的结点数}ans = std::min(ans, std::max(max_sz, n - sz[u])); // 计算去掉u后最大子树的节点数return sz[u]; // 返回以u为根的子树的结点数
}// 向下找找到最长路径和次长路径
int DFSDown(int u, int fa) {d1[u] = d2[u] = 0;for (const auto& [v, w] : e[u]) {if (v == fa) continue;int d = DFSDown(v, u) + 1;if (d >= d1[u]) {d2[u] = d1[u];d1[u] = d;p[u] = v; // 记录u的子节点是谁}else if (d > d2[u]) d2[u] = d;}return d1[u];
}// 向上查找,如果说 v 位于最长路,则比较次长路和向上走那个距离更远,更新up[v]
// 如果说 v 位于次长路,则比较最长路和向上走那个距离更远,更新up[v]
void DFSUp(int u, int fa) {for (const auto& [v, w] : e[u]) {if (v == fa) continue;if (p[u] == v) up[v] = std::max(up[u], d2[u]) + 1;else up[v] = std::max(up[u], d1[u]) + 1;DFSUp(v, u);}
}inline void Build(int u) {while (u) { // 获取路径(反向)a.push_back(u);u = from[u];}std::reverse(a.begin(), a.end()); // 变成从起点到终点
}inline void TreeL() { // 获得直径DFS(1, 0);dist[max_v] = 0;DFS(max_v, 0);Build(max_v);
}inline int TreeMid(int x) { // 获得中心DFSDown(x, 0);up[1] = 0;DFSUp(x, 0);int res = INF;Rep(i, 1, n + 1) res = std::min(res, std::max(up[i], d1[i]));std::cout << res << '\n';return res;
}inline int Centroid(int x) { // 获得返回以x为根的子树的重心std::fill(vis.begin(), vis.end(), false);ans = INF;int s = DFS(x); // 重载的DFS计算重心(s是以x为根的子树的结点数)// 找到最小的最大子树节点数对应的节点int centroid = -1;Rep(i, 1, n + 1) {if (!vis[i]) continue; // 只考虑子树中的节点int max_sub = std::max(sz[i], s - sz[i]); // s是子树大小if (max_sub == ans) {centroid = i;break;}}return centroid;
}inline void Init(int n) {e.resize(n + 1);sz.resize(n + 1);dist.resize(n + 1);d.resize(n + 1);d1.resize(n + 1);d2.resize(n + 1);up.resize(n + 1);p.resize(n + 1);from.resize(n + 1);vis.resize(n + 1, false);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/81262.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年【道路运输企业安全生产管理人员】考试题库及道路运输企业安全生产管理人员考试内容

一、考试概述 2025年道路运输企业安全生产管理人员考试题库由【安全生产模拟考试一点通】平台发布&#xff0c;涵盖安全生产法律法规、车辆技术管理、从业人员管理、应急预案编制等核心领域。考试重点考察考生对安全生产主体责任、风险管控、隐患排查等实务操作的掌握程度&…

分贝计在噪音污染源识别中的用途

分贝计在噪音污染源识别中的作用 噪音污染是现代社会面临的一个普遍问题&#xff0c;尤其在城市化进程加快的背景下&#xff0c;交通、工业、建筑和娱乐活动等产生的噪音对人们的生活质量和健康造成了严重影响。为了有效管理和控制噪音污染&#xff0c;首先需要准确识别噪音的…

deepin v23.1 搜狗输入法next配置中文输入法下默认用英文标点

deepin23.1下, fcitx5的 deepin next搜狗输入法的属性页无法配置中文状态下默认用英文标点, 但是可以改以下配置来实现这一点. 搜狗输入法运行期间&#xff0c;用户修改的配置被存储在以下位置&#xff1a; ~/.config/cpis/module/im/fcitx5/com.sogou.ime.ng.fcitx5.deepin/k…

C语言:在 Win 10 上,g++ 如何编译 gtk 应用程序

在 Windows 10 上使用 g&#xff08;或 gcc&#xff09;编译基于 GTK 的 C 语言程序是完全可行的&#xff0c;且相比 Tcc 更为推荐&#xff0c;因为 g&#xff08;GNU 编译器套件&#xff09;对 GTK 的支持更加完善&#xff0c;配置也更简单。以下是详细步骤和注意事项&#xf…

84.评论日记

原链接 这个视频我发了四五条评论。评论内容甚至和下面这个视频内的其他评论一样。 找了另外的账号也发了。 发现&#xff0c;无论是我这个账号&#xff0c;还是其他的账号&#xff0c;评论都无法看到。 我大胆猜测有一种机制&#xff0c;某些官号会被设置成一种高检测的等…

【RabbitMQ】整合 SpringBoot,实现工作队列、发布/订阅、路由和通配符模式

文章目录 工作队列模式引入依赖配置声明生产者代码消费者代码 发布/订阅模式引入依赖声明生产者代码发送消息 消费者代码运行程序 路由模式声明生产者代码消费者代码运行程序 通配符模式声明生产者代码消费者代码运行程序 工作队列模式 引入依赖 我们在创建 SpringBoot 项目的…

Python-92:最大乘积区间问题

问题描述 小R手上有一个长度为 n 的数组 (n > 0)&#xff0c;数组中的元素分别来自集合 [0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]。小R想从这个数组中选取一段连续的区间&#xff0c;得到可能的最大乘积。 你需要帮助小R找到最大乘积的区间&#xff0c;并输出这…

windows触摸板快捷指南

以下是结构化整理后的触控手势说明&#xff0c;采用清晰的层级划分和标准化表述&#xff1a; **触控手势操作规范****1. 单指操作****2. 双指操作****3. 三指操作****4. 四指操作** **优化说明&#xff1a;** 触控手势操作规范 1. 单指操作 手势功能描述等效操作单击滑动选择…

VSCode launch.json 配置参数详解

使用 launch.json 配置调试环境时&#xff0c;会涉及到多个参数&#xff0c;用于定义调试器的行为和目标执行环境。以下是一些常用的配置参数&#xff1a; 1、"type" &#xff1a;指定调试器的类型&#xff0c;例如 "node" 表示 Node.js 调试器&#xff0…

mAP、AP50、AR50:目标检测中的核心评价指标解析

在目标检测任务中&#xff0c;评价指标是衡量模型性能的核心工具。其中&#xff0c;mAP&#xff08;mean Average Precision&#xff09;、AP50&#xff08;Average Precision at IoU0.5&#xff09;和AR50&#xff08;Average Recall at IoU0.5&#xff09;是最常用的指标。本…

【论文阅读】A Survey on Multimodal Large Language Models

目录 前言一、 背景与核心概念1-1、多模态大语言模型&#xff08;MLLMs&#xff09;的定义 二、MLLMs的架构设计2-1、三大核心模块2-2、架构优化趋势 三、训练策略与数据3-1、 三阶段训练流程 四、 评估方法4-1、 闭集评估&#xff08;Closed-set&#xff09;4-2、开集评估&…

[已解决] LaTeX “Unicode character“ 报错 (中文字符处理)

问题&#xff1a; 写 LaTeX 文档&#xff0c;特别是包含中文时&#xff0c;经常遇到类似下图的 “Unicode character XXXXXX” 报错 (X) Unicode character 本 (U672C) LaTeX [行 xx, 列 x] (X) Unicode character 报 (U62A5) LaTeX [行 xx, 列 x] ...这通常意味着我们的 LaTe…

现货黄金跌破 3160 美元,市场行情剧烈波动​

在 5 月 16 日的交易时段中&#xff0c;现货黄金市场出现戏剧性变化&#xff0c;价格短时间内大幅跳水。截至当日 20:04&#xff0c;现货黄金短线下挫 20 美元&#xff0c;一举跌破 3160 美元 / 盎司&#xff0c;日内跌幅达 2.56%&#xff1b;纽约期金日内也大跌 2%&#xff0c…

智慧校园(含实验室)智能化专项汇报方案

该方案聚焦智慧校园(含实验室)智能化建设,针对传统实验室在运营监管、环境监测、安全管控、排课考勤等方面的问题,依据《智慧校园总体框架》等标准,设计数字孪生平台、实验室综合管理平台、消安电一体化平台三大核心平台,涵盖通信、安防、建筑设备管理等设施,涉及 395 个…

【Python爬虫 !!!!!!政府招投标数据爬虫项目--医疗实例项目文档(提供源码!!!)!!!学会Python爬虫轻松赚外快】

政府招投标数据爬虫项目--医疗实例项目文档 1. 项目概述1.1 项目目标1.2 技术栈2. 系统架构2.1 模块划分2.2 流程示意图3. 核心模块设计3.1 反爬处理模块(`utils/anti_crawler.py`)3.1.1 功能特性3.1.2 关键代码3.2 爬虫模块(`crawler/spiders/`)3.2.1 基类设计(`base_spi…

RabbitMQ是什么?应用场景有哪些?

RabbitMQ 是一款开源的消息代理中间件,基于 AMQP(高级消息队列协议)实现,用于在分布式系统中进行异步通信和消息传递。它通过将消息的发送者和接收者解耦,提高了系统的可扩展性、可靠性和灵活性。 核心特点 多协议支持:不仅支持 AMQP,还兼容 STOMP、MQTT 等多种消息协议…

RT Thread FinSH(msh)调度逻辑

文章目录 概要FinSH功能FinSH调度逻辑细节小结 概要 RT-Thread&#xff08;Real-Time Thread&#xff09;作为一款开源的嵌入式实时操作系统&#xff0c;在嵌入式设备领域得到了广泛应用。 该系统不仅具备强大的任务调度功能&#xff0c;还集成了 FinSH命令行系统&#xff0c…

我司助力高校打造「智慧创新AI学习中心」

为推动AI教育融合跨领域应用&#xff0c;东吴大学于2025年4月举行「智慧创新AI学习中心」揭牌仪式&#xff0c;并宣布正式启动AI特色课程与教学空间建置计画。此次建置由我司协助整体教室空间与设备规划&#xff0c;导入最新NVIDIA GeForce RTX 50系列桌上型电脑&#xff0c;并…

给你的matplotlib images添加scale Bar

​Scale Bar&#xff08;比例尺&#xff09;用于直观表示图像与实际物理尺寸&#xff08;如微米、毫米等&#xff09;的对应关系。例如&#xff0c;在显微镜图像中&#xff0c;比例尺可以标注“75μm”表示图中某线段对应的实际长度。 这里分享使用matplotlib中的imshow结合ma…

基于React的高德地图api教程004:线标记绘制、修改、删除功能实现

文章目录 4、线绘制4.1 绘制线标记4.1.1 开启线标记绘制模式4.1.2 绘制线标记4.1.3 关闭线标记模式4.2 可视化线标记数据面板4.3 修改线标记4.3.1 修改线标记路径4.3.2 修改线标记名称和颜色4.4 删除线标记4.5 定位线标记4.6 代码下载4.04、线绘制 4.1 绘制线标记 4.1.1 开启…