计蒜客G.选根

第一次做换根Dp的题,然后竟然AC了,还是有点小激动呢

这个是邹巨发给我的他写的一篇换根Dp的博客:邹巨博客链接,我个人觉得还是写的蛮不错的,尤其那张雨巨的图,反正就是歪瑞古德。

比赛传送门

题目描述

  • 样例输入:
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号点当根节点(基本任何题)
  • 然后我拿雨巨那张图来做解释吧
    在这里插入图片描述
  • 我先说一下我接下来会用到的变量,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值