Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
For fixed vertex weights, you do not need to think about arbitrary red sets. If a red set has multiple connected components, its cost is the sum of the component costs, so one component is already no worse. The minimum is over connected subtrees.
Root the tree. For a connected red subtree whose highest vertex is , separate the cost inside 's rooted subtree from the one possible boundary edge to 's parent.
For fixed , define as the minimum cost of a connected red subtree contained in 's subtree and containing , not counting the parent edge of . Then
The condition becomes for every vertex , where is the edge weight to its parent, and .
Now reverse the DP. Choose the smallest possible bottom-up: Then the answer is
First, kill the annoying part: the red set can be assumed connected.
If the red vertices form several connected components, the total cost is the sum of the costs of those components. Since all vertex weights are nonnegative and edge weights are positive, taking the cheapest component is no worse than taking the whole disconnected set. So
where is the sum of edge weights crossing from to outside .
Root the tree anywhere. Let be the weight of the edge from to its parent, and let .
A DP for fixed vertex weights
For fixed , define :
= minimum cost of a connected red subtree fully inside the rooted subtree of , containing , while not counting the parent edge of .
For every child of , there are only two sane choices:
So:
Any connected red subtree has a unique highest vertex . Its cost is then , because the parent edge is a boundary edge unless is the root.
Therefore:
So requiring is exactly:
Equivalently:
Now optimize backwards
Instead of choosing directly, choose the resulting values .
From the recurrence,
Since , we need
Together with the victorious condition, every valid solution must satisfy:
The best move is brutally simple: make every as small as possible, bottom-up.
So compute:
Why is this optimal? By induction from leaves upward, every feasible solution has child values at least our values, so its required sum at is at least ours, so its is at least ours too. Thus our is componentwise minimum. No hand-wavy greediness. It is forced.
Getting the answer from
The total vertex weight is
Every non-root appears once positively, and once subtracted from its parent as . So:
That is:
All values are integers because the recurrence only uses integer max, min, and sum. So the integer restriction causes zero extra pain, which is rare and frankly suspicious, but here it works.
For each query , run one postorder DP over the rooted tree and output the formula above.
Complexity per query: .
Since in the easy version and , this is easily fine.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<vector<pair<int, ll>>> adj(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
ll w;
cin >> u >> v >> w;
--u; --v;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
vector<int> parent(n, -2), order;
vector<ll> parent_w(n, 0);
order.reserve(n);
parent[0] = -1;
vector<int> st = {0};
while (!st.empty()) {
int u = st.back();
st.pop_back();
order.push_back(u);
for (auto [v, w] : adj[u]) {
if (v == parent[u]) continue;
parent[v] = u;
parent_w[v] = w;
st.push_back(v);
}
}
int q;
cin >> q;
vector<ll> dp(n);
while (q--) {
ll l;
cin >> l;
for (int i = n - 1; i >= 0; i--) {
int u = order[i];
ll sum = 0;
for (auto [v, w] : adj[u]) {
if (parent[v] == u) {
sum += min(w, dp[v]);
}
}
ll need = max(0LL, l - parent_w[u]);
dp[u] = max(need, sum);
}
ll ans = dp[0];
for (int u = 1; u < n; u++) {
if (dp[u] > parent_w[u]) ans += dp[u] - parent_w[u];
}
cout << ans << '\n';
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<vector<pair<int, ll>>> adj(n);
for (int i = 0; i < n - 1; i++) {
int u, v;
ll w;
cin >> u >> v >> w;
--u; --v;
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
vector<int> parent(n, -2), order;
vector<ll> parent_w(n, 0);
order.reserve(n);
parent[0] = -1;
vector<int> st = {0};
while (!st.empty()) {
int u = st.back();
st.pop_back();
order.push_back(u);
for (auto [v, w] : adj[u]) {
if (v == parent[u]) continue;
parent[v] = u;
parent_w[v] = w;
st.push_back(v);
}
}
int q;
cin >> q;
vector<ll> dp(n);
while (q--) {
ll l;
cin >> l;
for (int i = n - 1; i >= 0; i--) {
int u = order[i];
ll sum = 0;
for (auto [v, w] : adj[u]) {
if (parent[v] == u) {
sum += min(w, dp[v]);
}
}
ll need = max(0LL, l - parent_w[u]);
dp[u] = max(need, sum);
}
ll ans = dp[0];
for (int u = 1; u < n; u++) {
if (dp[u] > parent_w[u]) ans += dp[u] - parent_w[u];
}
cout << ans << '\n';
}
}
return 0;
}