洛谷P3128 [USACO15DEC]Max Flow P 題解 樹上差分(點差分)
阿新 • • 發佈:2021-12-18
題目連結:https://www.luogu.com.cn/problem/P3128
題目大意:
給定一個包含 \(n\) 個節點的樹,以及 \(k\) 次操作。每次操作你需要將一條路徑上的點權均加 \(1\)。求 \(k\) 次操作之後的最大點權。
解題思路:
樹上差分(點差分)。對於一條路徑的兩個端點 \(u\) 和 \(v\),設 \(p\) 是它們的LCA,設 \(p'\) 是 \(p\) 的父節點(若 \(p\) 為根節點則 \(p' = p\))。
開差分陣列 \(d\),則 \(d[u]\) 和 \(d[v]\) 依次加 \(1\),\(d[p]\) 和 \(d[p']\) 依次減 \(1\)
最後對這棵樹進行差分(dfs遍歷一遍即可)。
示例程式:
#include <bits/stdc++.h> using namespace std; const int maxn = 500050; int n, m, pa[maxn][21], dep[maxn]; vector<int> g[maxn]; int d[maxn], ans; void dfs(int u, int p) { dep[u] = dep[p] + 1; pa[u][0] = p; for (int i = 1; (1<<i) <= dep[u]; i ++) pa[u][i] = pa[ pa[u][i-1] ][i-1]; for (auto v : g[u]) if (v != p) dfs(v, u); } int lca(int x, int y) { if (dep[x] < dep[y]) swap(x, y); for (int i = 20; i >= 0; i --) { if (dep[ pa[x][i] ] >= dep[y]) x = pa[x][i]; if (x == y) return x; } for (int i = 20; i >= 0; i --) { if (pa[x][i] != pa[y][i]) { x = pa[x][i]; y = pa[y][i]; } } return pa[x][0]; } void dfs2(int u, int p) { for (auto v : g[u]) if (v != p) dfs2(v, u), d[u] += d[v]; } int main() { ios::sync_with_stdio(0); cin >> n >> m; for (int i = 1; i < n; i ++) { int u, v; cin >> u >> v; g[u].push_back(v); g[v].push_back(u); } dfs(1, 0); while (m --) { int u, v, p; cin >> u >> v; p = lca(u, v); d[u] ++; d[v] ++; d[p] --; d[pa[p][0]] --; } dfs2(1, 0); for (int i = 1; i <= n; i ++) ans = max(ans, d[i]); cout << ans << endl; return 0; }