第一次做换根Dp的题,然后竟然AC了,还是有点小激动呢
这个是邹巨发给我的他写的一篇换根Dp的博客:邹巨博客链接,我个人觉得还是写的蛮不错的,尤其那张雨巨的图,反正就是歪瑞古德。
![题目描述](https://i-blog.csdnimg.cn/blog_migrate/209f24b275eefa15319d4867cb50cc50.png)
2
6
5 2 8 1 7 8
4 5
5 6
2 5
1 3
4 3
5
1 1 1 1 1
1 2
2 3
3 4
4 5
102 100 81 76 73 88
15 12 11 12 15
- 这个我先说一下我的思路吧,我比较喜欢拿1号点当根节点(基本任何题)
- 然后我拿雨巨那张图来做解释吧
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/825a0f607b8e45d3a2d7fa5c2872d042.png)
- 我先说一下我接下来会用到的变量,fa[i]也就是答案,dist[i]表示其加上子节点的贡献度,dep[i]记录以1号点为根节点的深度(这个其实在这个题用到的不多);sum 代表的是所有的w[i]之和
- fa[x]已知,dist[x]我们也算出来了,dist[y]也算出来了
- 我们先想一下fa[x]是怎么算的
- fa[x] = 子树1的贡献度 + ···· + 子树y的贡献度 + ···· + 子树3的贡献度
- 当我们把y点作为根节点的时候,那么x以及其他子树都会离y远了一个整体的距离也就是sum - dist[y]
- 那么我们求fa[y],势必与fa[x],紧密联系在一起,所以我们得判断fa[x]和fa[y]之间的关系
- 所以我们在 fa[x] 的基础上加上 sum - dist[y],也就是 fa[x] + sum - dist[y],那么当x的做根的时候,假设y的子节点到x的整体距离为2,y到x的距离为1,那么此时y的子节点到y的整体距离为1了,对吧;
- 所以我们fa[y] = fa[x] + (sum - dist[y]) - dist[y];
- 下面是我AC这道题目的代码
- 变量可能有些许与我上面说到的思路里面的变量不一样
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e6 + 10;
int t;
int n;
int w[N];
vector<int>v[N];
long long dist[N];
long long fa[N];
long long dep[N];
long long sum = 0;
void dfs1(int u,int fa)
{
dist[u] = w[u];
dep[u] = dep[fa] + 1;
for(auto &x : v[u]){
if(x != fa){
dfs1(x,u);
dist[u] += dist[x];
}
}
}
void dfs2(int u,int f)
{
for(auto &x : v[u]){
if(x != f){
fa[x] = fa[u] + (sum - dist[x])- dist[x] ;
dfs2(x,u);
}
}
}
int main()
{
scanf("%d",&t);
while(t --)
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)
{
scanf("%d",&w[i]);
v[i].clear();
fa[i] = 0;
}
sum = 0;
for(int i = 0;i < n - 1;i ++)
{
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
dep[1] = 0;
dfs1(1,1);
for(int i = 1;i <= n;i ++)
{
fa[1] += dep[i] * w[i];
sum += w[i];
}
dfs2(1,1);
for(int i = 1;i <= n;i ++)
{
printf("%lld ",fa[i]);
}
printf("\n");
}
return 0;
}