基礎樹論
簡報自己讀不懂很正常他偏抽象
基本概念
存圖&遍歷
int n; vector<vector<int>> adj(n + 1); for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); // 無向邊要建雙向 }
void dfs(int u, int p) { // process node u for (int v : adj[u]) { if (v == p) continue; // 防止往回走 dfs(v, u); } } // main dfs(root, -1); // 通常根節點是 1 可以直接 dfs(1, -1);
auto dfs = [&](auto self, int u, int p) -> void { // process node u for (int v : adj[u]) { if (v == p) continue; self(self, v, u); } }; // 記得加分號 // main dfs(dfs, root, -1);
vector<int> dist(n + 1, -1); queue<int> q; dist[start] = 0; q.push(start); while (!q.empty()) { int u = q.front(); q.pop(); for (int v : adj[u]) { if (dist[v] == -1) { dist[v] = dist[u] + 1; q.push(v); } } }
子樹大小
題目
void dfs(int u, int p) { cnt[u] = 1; for (int v : adj[u]) { if (v == p) continue; dfs(v, u); // 要先處理完子節點再更新狀態 cnt[u] += cnt[v]; } }
樹直徑
題目
int s = 1, dia = 0; vector<int> depth(n + 1); auto dfs = [&](auto self, int u, int p) -> void { if (depth[u] > dia) { dia = depth[u]; s = u; } for (int v : adj[u]) if (v != p) { depth[v] = depth[u] + 1; self(self, v, u); } }; dfs(dfs, 1, -1); depth[s] = 0; dfs(dfs, s, -1);
auto dfs = [&](auto self, int u, int p) -> int { int mx = 0, mx2 = 0; for (int v : adj[u]) if (v != p) { int h = self(self, v, u) + 1; if (h > mx) { mx2 = mx; mx = h; } else if (h > mx2) { mx2 = h; } } dia = max(dia, mx + mx2); return mx; }; dfs(dfs, 1, -1);
全源最長路
題目
auto dfs = [&](auto self, int u, int p) -> void { for (int v : adj[u]) if (v != p) { self(self, v, u); if (maxDist[v] + 1 > maxDist[u]) { secMax[u] = maxDist[u]; maxDist[u] = maxDist[v] + 1; path[u] = v; } else if (maxDist[v] + 1 > secMax[u]) { secMax[u] = maxDist[v] + 1; } } };
auto dfs2 = [&](auto self, int u, int p) -> void { for (int v : adj[u]) if (v != p) { if (path[u] == v) { // maxDist[u] is the path toward v if (secMax[u] + 1 > maxDist[v]) { secMax[v] = maxDist[v]; maxDist[v] = secMax[u] + 1; path[v] = u; } else if (secMax[u] + 1 > secMax[v]) { secMax[v] = secMax[u] + 1; } } else { // maxDist[u] + 1 >= maxDist[v] in this case // cause if not, then maxDist[u] should calculated as maxDist[v] + 1 in first dfs secMax[v] = maxDist[v]; maxDist[v] = maxDist[u] + 1; path[v] = u; } self(self, v, u); } };
換根 DP
題目
auto dfs = [&](auto self, int u, int p, int dist) -> void { ans[1] += dist; sz[u] = 1; // 計算子樹大小 for (int v : adj[u]) if (v != p) { self(self, v, u, dist + 1); sz[u] += sz[v]; } };
auto dfs2 = [&](auto self, int u, int p) -> void { for (int v : adj[u]) if (v != p) { ans[v] = ans[u] - sz[v] + (n - sz[v]); self(self, v, u); } };
倍增法
題目
vector<vector<int>> succ(__lg(n) + 1, vector<int>(n + 1)); vector<int> depth(n + 1); auto dfs = [&](auto self, int u) -> void { for (int v : adj[u]) { succ[0][v] = u; depth[v] = depth[u] + 1; for (int i = 1; i <= __lg(depth[v]); i++) { succ[i][v] = succ[i - 1][succ[i - 1][v]]; } self(self, v); } };
for (int i = lg(k); i >= 0; i--) { if (k & (1 << i)) { x = succ[i][x]; } }
最低共通祖先
題目
auto lca = [&](int x, int y) -> int { if (depth[x] < depth[y]) swap(x, y); while (depth[x] > depth[y]) { x = succ[lg(depth[x] - depth[y])][x]; } if (x == y) return x; // 如果定位到同深度時已經找到 要先返回 for (int i = lg(depth[x]); i >= 0; i--) { if (succ[i][x] != succ[i][y]) { x = succ[i][x], y = succ[i][y]; } } return succ[0][x]; };
vector<int> timer(2 * n + 1), id(n + 1), depth(n + 1); auto dfs = [&](auto self, int u) -> void { id[u] = cnt; timer[cnt++] = u; for (int v : adj[u]) { depth[v] = depth[u] + 1; self(self, v); timer[cnt++] = u; } };
vector<vector<int>> st(lg(cnt) + 1, vector<int>(cnt + 1)); for (int i = 1; i <= cnt; i++) st[0][i] = i; for (int i = 1; i <= lg(cnt); i++) { int len = 1 << (i - 1); for (int j = 1; j + len <= cnt; j++) { int a = st[i - 1][j], b = st[i - 1][j + len]; if (depth[timer[a]] <= depth[timer[b]]) st[i][j] = a; else st[i][j] = b; } }
int l = id[a], r = id[b]; if (l > r) swap(l, r); int k = lg(r - l + 1); a = st[k][l], b = st[k][r - (1 << k) + 1]; if (depth[timer[a]] <= depth[timer[b]]) { cout << timer[a] << '\n'; } else cout << timer[b] << '\n';
樹壓平
題目
auto dfs = [&](auto self, int u, int p) -> void { st[u] = ++cnt; for (int v : adj[u]) if (v != p) { self(self, v, u); } en[u] = cnt; };
BIT tree(n); for (int i = 1; i <= n; i++) { tree.modify(st[i], v[i]); }
if (o == 1) { // modify int x; cin >> x; tree.modify(st[s], x); } else { cout << tree.query(en[s]) - tree.query(st[s] - 1) << '\n'; }