图论模板(部分)
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 0x80000000
const 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; // 加入到点集C
for(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 0x80000000
const 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 0x80000000
const 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 0x80000000
const 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 0x80000000
const 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+1
int u = 2 * x + a;
int v = 2 * y + b;
e[u ^ 1].push_back(v); // ¬u → v
e[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 0x80000000
const 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 0x80000000
const 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);
}