换根DP
换根DP
无根树上的DP?
相关资料
第一个例题距离和
洛谷类似题 - 洛谷 P3478 [POI2008] STA-Station
有一棵 \(n (1 \le n \le 100000)\) 个点的无根树,请求出每个点到其他所有点的距离的和。定义两个点的距离为它们的简单路径上经过了多少条边。
朴素的思想就是枚举每个点作为根遍历整棵树,那么 \(O(n^2)\) 即可解决该问题,但是可以注意到,父节点和子节点各自作为根节点时,数值关系可以传递
定义两个数组 f 和 v:
\(f[i]\) 表示以节点 i 为根的子树中的点到 i 的距离和
\(v[i]\) 表示把节点 i 的父亲 x 作为节点 i 的孩子节点时以 x 为根的子树中的点到 i 的距离和
那么第一次 dfs 时可以维护数组 f 的值,\(f[i] = sz[i] - 1 + \sum f[j]\),j 为 i 的子节点,sz[i]为以 i 为根的子树节点数
第二次 dfs 可以维护数组 v 的值,$ v[t] = v[u] + f[u] - f[t] - sz[t] + n - sz[t]; $,t 为 u 的子节点
未验证的代码
int n, sz[N], f[N], v[N];
vector<int> e[N];
/*
f[i] 表示以节点 i 为根的子树中的点到 i 的距离和
v[i] 表示把节点 i 的父亲 x 作为节点 i 的孩子节点时以 x 为根的子树中的点到 i 的距离和
*/
void dfs1(int u, int fa){
sz[u] = 1;
f[u] = 0;
for(auto v : e[u]){
if(v == fa) continue;
dfs1(v, u);
sz[u] += sz[v];
f[u] += f[v];
}
f[u] += sz[u] - 1;
return ;
}
void dfs2(int u, int fa){
for(auto t : e[u]){
if(t == fa) continue;
v[t] = v[u] + f[u] - f[t] - sz[t] + n - sz[t];
dfs2(t, u);
}
return ;
}
void solve(){
cin >> n;
for(int i = 1; i < n; ++ i){
int u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1, 0);
dfs2(1, 0);
for(int i = 1; i <= n; ++ i){
cout << f[i] + v[i] << '\n';
}
return ;
}
第二个例题 流
依旧是考虑以节点 x 为根的子树可以承担的最大流量,以及将节点 x 的父节点作为子节点时可以承担的最大流量,此问题还得考虑边对转移的影响
第三个例题 最长路径
类似于树的直径?
例题
综合运用
模板题
-
求以哪一个点为根时所有节点深度和最大 洛谷 P3478 [POI2008] STA-Station // Qiansui_code
本题做法与相关链接例题 距离和 一样 -
上题的小小强化版 洛谷 P2986 [USACO10MAR] Great Cow Gathering G // Qiansui_code
本题基本做法与上题一样,就是距离的定义发生了变化 -
简单换根DP cf 627 div.3 F. Maximum White Subtree // Qiansui_code
白色节点权值记为1,黑色节点权值记为-1,再跑标准的换根DP就行 -
稍微难一点的换根DP 洛谷 P3047 [USACO12FEB] Nearby Cows G // Qiansui_code
对每个节点都记录距离自己 0 ~ k 距离的节点权值和,先 dfs 一遍统计子树,再 dfs 一遍更新答案 -
再难一点的换根DP cf 899 div.2 D. Tree XOR // Qiansui_code
首先需要发现一点的是,对于一个有根树,无论你怎么执行操作,对于一组父子节点 uv,只要你不选择节点 v 进行操作,那么节点 u 和节点 v 的权值就永远不变。所以想要所有节点权值均相等,我们从根开始 dfs 遍历,遇到权值不相等的子节点则选择节点 v 异或 $a[u] \oplus a[v] $即可获得一次答案
不可能对于每个节点都做一次dfs,那么考虑换根DP的做法。可以发现,当根节点从节点 u 传递给子节点 v 的时候,受到影响的答案只有节点 u 和节点 v 之间的关系。原本选择节点 v 进行修改操作,现在改为节点 u 进行修改操作即可,即 \(ans[v] = ans[u] - (a[u] \oplus a[v]) \times sz[v] + (a[u] \oplus a[v]) \times (n - sz[v])\)
所以利用换根DP,可以 \(O(n)\) 处理该问题