Radio Prize(换根dp)

题意

给定一棵无根树,树上每个节点有点权 t i t_i ti,两点之间有边权 w i w_i wi。定义从 u u u v v v的距离 d u , v d_{u,v} du,v为从 u u u v v v路径的边权和,花费为 ( t u + t v ) ∗ d u , v (t_u + t_v) * d_{u,v} (tu+tv)du,v。对每个点,都要求出到其他点花费的总和。

数据范围

1 ≤ n ≤ 100000 1 \leq n \leq 100000 1n100000

思路

首先先把花费的式子拆开,即 p u , v = ( t u + t v ) ∗ d u , v = t u ∗ d u , v + t v ∗ d u , v p_{u,v} = (t_u + t_v) * d_{u,v} = t_u*d_{u,v} + t_v*d_{u,v} pu,v=(tu+tv)du,v=tudu,v+tvdu,v。假设要求 u u u到其他点花费的总和,那么 P u = t u ∗ ∑ v d u , v + ∑ v ( t v ∗ d u , v ) P_u = t_u* \sum_{v} d_{u,v} + \sum_v(t_v*d_{u,v}) Pu=tuvdu,v+v(tvdu,v)。令 d i s t u = ∑ v d u , v dist_u = \sum_{v} d_{u,v} distu=vdu,v d i s t v a l u = ∑ v ( t v ∗ d u , v ) distval_u = \sum_v(t_v*d_{u,v}) distvalu=v(tvdu,v),则 P u = t u ∗ d i s t u + d i s t v a l u P_u = t_u*dist_u + distval_u Pu=tudistu+distvalu
我们再探讨 d i s t u dist_u distu以及 d i s t v a l u distval_u distvalu的实际意义, d i s t u dist_u distu表示其他点到 u u u点的距离总和, d i s t v a l u distval_u distvalu表示其他点到 u u u点的距离与点权乘积之和。我们可以先预处理出以 u u u为根的子树中节点到 u u u点的距离总和以及以 u u u为根的子树中节点到 u u u点的距离与点权成绩之和。假设父节点为 x x x,子节点为 y y y,则预处理的转移方程为 d i s t [ x ] = d i s t [ y ] + s u m n [ y ] ∗ w [ i ] dist[x] = dist[y] + sumn[y] * w[i] dist[x]=dist[y]+sumn[y]w[i],这里的 s u m n sumn sumn数组指的是子树中节点的数量。 d i s t v a l [ x ] = d i s t v a l [ y ] + s u m v [ y ] ∗ w [ i ] distval[x] = distval[y] + sumv[y]*w[i] distval[x]=distval[y]+sumv[y]w[i],这里的 s u m v sumv sumv数组指的是子树中节点点权之和。
然后我们再通过换根dp的方式,转移出 d i s t , d i s t v a l dist,distval dist,distval数组。假设父节点为 x x x,子节点为 y y y,转移方程为 d i s t [ y ] = d i s t [ x ] − s u m n [ y ] ∗ w [ i ] + ( n − s u m n [ y ] ) ∗ w [ i ] dist[y] = dist[x] - sumn[y]*w[i] + (n - sumn[y])*w[i] dist[y]=dist[x]sumn[y]w[i]+(nsumn[y])w[i],这里的 n n n指的是节点的数量。 d i s t v a l [ y ] = d i s t v a l [ x ] − s u m v [ y ] ∗ w [ i ] + ( V − s u m v [ y ] ) ∗ w [ i ] distval[y] = distval[x] - sumv[y]*w[i] + (V - sumv[y]) * w[i] distval[y]=distval[x]sumv[y]w[i]+(Vsumv[y])w[i],这里的 V V V指的是所有节点的点权总和。
理顺一下思路,就是先预处理出 s u m n , s u m v , d i s t , d i s t v a l sumn,sumv,dist,distval sumn,sumv,dist,distval四个数组,然后再通过树形dp的方式更新 d i s t , d i s t v a l dist,distval dist,distval两个数组,最后再通过推出的公式计算出答案。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, M = 2 * N;

typedef long long ll;

int n;
ll val[N];
int h[N], e[M], ne[M], idx;
ll w[M];
ll sumn[N], sumv[N];
ll dist[N], distval[N];
ll sumvalue;

void add(int a,int b,ll c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void dfs1(int u,int fa)
{
    for(int i=h[u];~i;i=ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dfs1(j,u);
        sumn[u] += sumn[j];
        sumv[u] += sumv[j];
        dist[u] += dist[j] + sumn[j] * w[i];
        distval[u] += distval[j] + sumv[j] * w[i];
    }
    sumn[u] ++;
    sumv[u] += val[u];
}

void dfs2(int u,int fa)
{
    for(int i=h[u];~i;i=ne[i]){
        int j = e[i];
        if(j == fa) continue;
        dist[j] = dist[u] - w[i] * sumn[j] + (n - sumn[j]) * w[i];
        distval[j] = distval[u] - w[i] * sumv[j] + (sumvalue - sumv[j]) * w[i];
        dfs2(j,u);
    }
}

int main()
{
    cin >> n;
    memset(h,-1,sizeof(h));
    for(int i=1;i<=n;i++){
        cin >> val[i];
        sumvalue += val[i];
    }
    for(int i=1;i<n;i++){
        int a,b,c;
        cin >> a >> b >> c;
        add(a,b,c), add(b,a,c);
    }
    dfs1(1,-1);
    dfs2(1,-1);
    for(int i=1;i<=n;i++) cout << distval[i] + val[i] * dist[i] << "\n";
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值