2021年度训练联盟热身训练赛第一场 I. Full Depth Morning Show

链接

https://ac.nowcoder.com/acm/contest/12606/I

题意

d u , v d_{u,v} du,v 为节点 u u u 到节点 v v v 的最短距离, t u t_u tu 为节点 u u u 的权值,对树上任意节点 u u u ∑ v = 1 n [ d u , v ∗ ( t u + t v ) ] \sum_{v=1}^{n}{[d_{u,v}*(t_u+t_v)]} v=1n[du,v(tu+tv)]

思路

换根 DP。

r e s u = ∑ v = 1 n [ d u , v ∗ ( t u + t v ) ] res_u=\sum_{v=1}^{n}{[d_{u,v}*(t_u+t_v)]} resu=v=1n[du,v(tu+tv)]

r e s u = ∑ v = 1 n [ d u , v ∗ ( t u + t v ) ] = t u ∗ ∑ v = 1 n d u , v + ∑ v = 1 n ( d u , v ∗ t v ) res_{u}=\sum_{v=1}^{n}{[d_{u,v}*(t_u+t_v)}]=t_u*\sum_{v=1}^{n}{d_{u,v}}+\sum_{v=1}^{n}{(d_{u,v}*t_v)} resu=v=1n[du,v(tu+tv)]=tuv=1ndu,v+v=1n(du,vtv)

a u = ∑ v = 1 n d u , v a_u=\sum_{v=1}^{n}{d_{u,v}} au=v=1ndu,v b u = ∑ v = 1 n ( d u , v ∗ t v ) b_u=\sum_{v=1}^{n}{(d_{u,v}*t_v)} bu=v=1n(du,vtv) s z u sz_u szu 为节点 u u u 的子树节点数, s u m u sum_u sumu 为节点 u u u 的子树节点权值和, u ′ u' u 为节点 u u u 的儿子节点:

a u ′ = a u − d u , u ′ ∗ s z u + d u , u ′ ∗ ( n − s z u ′ ) = a u + d u , u ′ ∗ ( n − 2 ∗ s z u ′ ) a_{u'}=a_u-d_{u,u'}*sz_u+d_{u,u'}*(n-sz_{u'})=a_u+d_{u,u'}*(n-2*sz_{u'}) au=audu,uszu+du,u(nszu)=au+du,u(n2szu)

b u ′ = b u − d u , u ′ ∗ s u m u + d u , u ′ ∗ ( ∑ i = 1 n t i − s u m u ′ ) = b u + d u , u ′ ∗ ( ∑ i = 1 n t i − 2 ∗ s u m u ′ ) b_{u'}=b_u-d_{u,u'}*sum_u+d_{u,u'}*(\sum_{i=1}^{n}{t_i}-sum_{u'})=b_u+d_{u,u'}*(\sum_{i=1}^{n}{t_i}-2*sum_{u'}) bu=budu,usumu+du,u(i=1ntisumu)=bu+du,u(i=1nti2sumu)

r e s u ′ = t u ′ ∗ a u ′ + b u ′ res_{u'}=t_{u'}*a_{u'}+b_{u'} resu=tuau+bu

两次 DFS,以 节点 1 1 1 为根,先计算出 a 1 a_1 a1 b 1 b_1 b1,再推出其余节点的 r e s res res O ( n ) O(n) O(n) 求解。

代码

#include <bits/stdc++.h>
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
// head
const int N=1e5+5;
int n;
LL t[N],d[N],sz[N],sum[N],a[N],b[N],res[N];
VPII g[N];
void dfs1(int u,int fa) {
    sz[u]=1;
    sum[u]=t[u];
    for(auto x:g[u]) {
        int v=x.FI,w=x.SE;
        if(v==fa) continue;
        d[v]=d[u]+w;
        a[1]+=d[v];
        b[1]+=d[v]*t[v];
        dfs1(v,u);
        sz[u]+=sz[v];
        sum[u]+=sum[v];
    }
}
void dfs2(int u,int fa) {
    for(auto x:g[u]) {
        int v=x.FI,w=x.SE;
        if(v==fa) continue;
        a[v]=a[u]+w*(n-2*sz[v]);
        b[v]=b[u]+w*(sum[1]-2*sum[v]);
        res[v]=a[v]*t[v]+b[v];
        dfs2(v,u);
    }
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>t[i];
    for(int i=1;i<n;i++) {
        int u,v,w;
        cin>>u>>v>>w;
        g[u].EB(v,w);
        g[v].EB(u,w);
    }
    dfs1(1,0);
    dfs2(1,0);
    res[1]=a[1]*t[1]+b[1];
    for(int i=1;i<=n;i++) cout<<res[i]<<'\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值