如何维护树上差分
多次对树上的一些路径做加法操作,然后询问某个点或某条边经过操作后的值,就要考虑树上差分了。
差分之后的树满足点 u 的子树和等于 u 点原来的点权。
- 点差分
例如,初态树上的各点点权为 0,现对路径 (x, y) 上的点均做 +1 操作,等价于 。
- 边差分
例如,初态树上的各点点权为 0,现对路径 (x, y) 上的边均做 +1 操作。边权操作比较困难,通常把点权下移给节点,变为点权操作,等价于 。
- 初态的各点点权不为 0 如何初始化 ?
对于叶子结点 leaf,差分值 = 点权值 。
对于非叶子结点 u,它的差分值 加上所有儿子的差分子树和 = , 所以差分值 = - 。
例题一 P3128 [USACO15DEC] Max Flow P
#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int N = 5E4 + 10, LOG = __lg(N) + 3;
vector<int> e[N];
int n, m, dep[N], fa[N][LOG], w[N];
// 倍增预处理
void dfs(int u, int f) {
dep[u] = dep[f] + 1;
fa[u][0] = f;
for(int i = 1; i < LOG; i ++)
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(auto v : e[u]) {
if(v != f) dfs(v, u);
}
}
// 求 lca
int lca(int u, int v) {
if(dep[u] < dep[v]) 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 u;
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 ans = -1E9;
void dfs2(int u, int f) {
for(auto v : e[u]) {
if(v == f) continue ;
dfs2(v, u);
w[u] += w[v];
}
ans = max(ans, w[u]);
}
void solve(){
cin >> n >> m;
for(int i = 1; i < n; i ++){
int x, y;
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1, 0); // 预处理倍增
for(int i = 1; i <= m; i ++){
int x, y;
cin >> x >> y;
int l = lca(x, y);
w[x] ++, w[y] ++, w[l] --, w[fa[l][0]] --;
}
dfs2(1, 0); // 统计答案
cout << ans;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}