题意
给定一棵无根树,树上每个节点有点权 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 1≤n≤100000
思路
首先先把花费的式子拆开,即
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=tu∗du,v+tv∗du,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=tu∗∑vdu,v+∑v(tv∗du,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(tv∗du,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=tu∗distu+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]+(n−sumn[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]+(V−sumv[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;
}