Radio Prize(换根dp)

    科技2022-07-13  131

    Radio Prize

    题意数据范围思路代码

    题意

    给定一棵无根树,树上每个节点有点权 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; }
    Processed: 0.012, SQL: 8