思路:考虑dp。
d
p
[
u
]
[
0
/
1
]
dp[u][0/1]
dp[u][0/1]表示
u
u
u的点权作为父边的
m
i
n
/
m
a
x
min/max
min/max。考虑转移,我们需要枚举当前节点修改后的权值,显然这个权值只会是与
u
u
u相连的所有边的边权和修改前
u
u
u的点权中的一个。
我们只需要把子树按照边权排序,然后依次枚举点权,当与该点相连的子树的边权等于点权时需要特判取:
m
i
n
(
d
p
[
v
]
[
1
]
,
d
p
[
v
[
[
0
]
)
min(dp[v][1], dp[v[[0])
min(dp[v][1],dp[v[[0]), 不相等的部分按照状态转移即可,可以通过维护两个指针实现。实现的细节看具体代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, int> P;
const int MAXN = 1e5 + 10;
const ll INF = 1e17;
struct edge {int to, we;};
vector<edge> g[MAXN];
int n;
int c[MAXN], wn[MAXN];
ll dp[MAXN][2], pre[MAXN], sum[MAXN];
bool cmp(int x, int y) { return pre[x] < pre[y]; }
ll cal(int u, int val) {
if(val <= wn[u])
return 1ll * c[u] * (wn[u] - val);
else
return 1ll * c[u] * (val - wn[u]);
}
void dfs(int u, int fa) {
dp[u][1] = dp[u][0] = INF;
vector<int> vec, val;
for(auto e : g[u]) {
int v = e.to;
int we = e.we;
if(v == fa) continue;
pre[v] = we;
vec.push_back(v); val.push_back(we);
dfs(v, u);
}
val.push_back(wn[u]);
val.push_back(pre[u]);
sort(vec.begin(), vec.end(), cmp);
sort(val.begin(), val.end());
int l = 0, r = 0;
ll p = 0, s = 0;
for(int i = 0; i < vec.size(); i++) {
sum[i+1] = sum[i] + min(dp[vec[i]][1], dp[vec[i]][0]);
s += dp[vec[i]][1];
}
for(int i = 0; i < val.size(); i++) {
while(l < vec.size() && pre[vec[l]] < val[i]) {
p += dp[vec[l]][0];
l++;
}
while(r < vec.size() && pre[vec[r]] <= val[i]) {
s -= dp[vec[r]][1];
r++;
}
ll w = cal(u, val[i]);
ll c = p + s + sum[r] - sum[l] + w;
if(val[i] <= pre[u]) dp[u][0] = min(dp[u][0], c);
if(val[i] >= pre[u]) dp[u][1] = min(dp[u][1], c);
}
}
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
g[i].clear();
}
for(int i = 1; i <= n; i++) {
scanf("%d", &c[i]);
}
for(int i = 1; i <= n; i++) {
scanf("%d", &wn[i]);
}
for(int i = 1; i < n; i++) {
int u, v, we; scanf("%d %d %d", &u, &v, &we);
g[u].push_back({v, we});
g[v].push_back({u, we});
}
pre[1] = 0;
dfs(1, 0);
printf("%lld\n", min(dp[1][0], dp[1][1]));
}
int main() {
int t = 1; scanf("%d", &t);
while(t--) {
solve();
}
return 0;
}