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.
Stop thinking about a deal as a pair of vertices that magically wants to get closer. After the swaps, a deal is sealed iff there is some edge whose two endpoints end up with the same badge. So the score is really a sum over edges in the final arrangement.
Root the tree. For a node , if the edge to its parent is not chosen, then the badge that finally sits on can only be: If the parent edge is chosen, then the badge on is forced to be . That's a tiny state space per node — way smaller than it first looks.
For a child of , suppose you already know the best value inside 's subtree for every possible final badge on . If the edge is not selected and the final badge on is some fixed label , then the best contribution of child is where is the best value in 's subtree regardless of the badge on , and is the best value when ends with label . Why? Either edge is not equal, or you force it to be — and collect .
Now define Then the two important cases are:
The last optimization is the whole damn problem. Write So for node , where is nonzero only for labels that actually appear in child 's state map. But those labels are only and labels of children of , so the total number of stored states over the whole tree is linear. Aggregate the positive gains by label at each node, and you get an DP per test case.
The statement talks about pairs of equal badges. Cute. But the score after the reshuffle is simply:
So instead of tracking each deal separately, track what badge ends up on each endpoint of each edge.
Because chosen edges form a matching, every vertex either:
That means every final badge moves by at most one edge. This is the key thing that makes the DP small instead of disgusting.
Root the tree arbitrarily, say at vertex .
Consider a node .
If the edge to its parent is not selected, then the final badge on can only be:
So the set of possible final badges on is just
That is only possibilities.
If the edge is selected, then swaps with its parent, so the final badge on is forced to be
That’s it. No wild badge teleports, no cursed global dependencies. The tree is behaving for once.
For each node , we maintain two kinds of information.
For every label that can end up on , define:
and the edge to the parent is not selected.
Also define
This is “best possible if parent doesn’t care what badge sits on ”.
Define
Then cannot swap with any child, and the final badge on is fixed to .
Fix a node , a child , and suppose the final badge on is some label .
If edge is not selected, then the child subtree can be arranged in any state with parent-edge unused. Two options:
So the best contribution of child is
Now sum over children:
Interpretation: if the final badge on is fixed to , and is not matched to any child, this is the best total contribution of all child sides.
Then the final badge on is , and doesn’t swap with any child.
So:
Let’s call this value
Then:
For all other children , we still use the “edge not selected” contribution with label on .
So:
That subtraction removes child ’s old “not selected” contribution, because now edge is selected instead.
If is matched to its parent, then the final badge on is forced to be , and cannot match any child.
So simply:
Very clean. Suspiciously clean, even. But it’s right.
Naively, to compute for every relevant , you’d scan all children every time. On a star, that’s how you lose 3 seconds and your dignity.
Rewrite:
Define
Then
The first part is a constant for node .
The second part only depends on labels for which child actually has a state . But what labels can appear on ?
Only:
So the number of stored labels for node is at most .
Hence the total number of states over the whole tree is
That means we can aggregate all positive gains by label in linear total time.
For each node :
While processing node bottom-up:
The answer for the whole test case is simply
No parent above the root, no extra nonsense.
We prove by induction on subtree size.
For a leaf, everything is trivial:
Now assume all children of are solved correctly.
If the parent edge of is not selected, then any valid configuration falls into exactly one of these mutually exclusive cases:
Those are the only possibilities because selected edges form a matching.
For each fixed final label on , every child with edge not selected contributes independently, and the best value is exactly
by the induction hypothesis.
If one child is selected, then its contribution must be replaced by the matched-to-parent value , plus the selected-edge reward if .
So the formulas for are exact, not heuristic.
If the parent edge of is selected, then cannot select a child, and the final badge on is fixed to , so
Thus all transitions are correct. Induction closes, and the root answer is optimal.
Let be the number of vertices.
Each node stores states only for labels of itself and its children, so the total number of stored states is
Each state entry is processed only a constant number of times.
So the total complexity per test case is
and over all test cases:
Memory is also linear:
Which is very nice, because anything slower here would get smacked by the limits.
#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;
int N = 2 * n;
vector<int> w(n + 1);
for (int i = 1; i <= n; ++i) cin >> w[i];
vector<int> a(N + 1);
for (int i = 1; i <= N; ++i) cin >> a[i];
vector<vector<int>> g(N + 1);
for (int i = 0; i < N - 1; ++i) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
// Root the tree at 1.
vector<int> par(N + 1, 0), order;
vector<vector<int>> children(N + 1);
order.reserve(N);
order.push_back(1);
par[1] = 0;
for (int i = 0; i < (int)order.size(); ++i) {
int v = order[i];
for (int to : g[v]) {
if (to == par[v]) continue;
par[to] = v;
children[v].push_back(to);
order.push_back(to);
}
}
// DP data.
vector<ll> bestBase(N + 1, 0); // B_v
vector<ll> up(N + 1, 0); // U_v
vector<ll> sameSelf(N + 1, 0); // S_v(a_v)
vector<vector<pair<int, ll>>> states(N + 1); // (label, S_v(label))
// Temporary arrays indexed by label 1..n.
vector<ll> accGain(n + 1, 0), bestForLabel(n + 1, 0);
vector<int> visGain(n + 1, 0), visBest(n + 1, 0);
int stampGain = 0, stampBest = 0;
for (int it = N - 1; it >= 0; --it) {
int v = order[it];
// Build F_v(L) = sum_c g_c(L) as baseSum + accumulated gains for label L.
++stampGain;
ll baseSum = 0;
for (int c : children[v]) {
baseSum += bestBase[c];
for (auto [lab, val] : states[c]) {
ll gain = val + (ll)w[lab] - bestBase[c];
if (gain <= 0) continue;
if (visGain[lab] != stampGain) {
visGain[lab] = stampGain;
accGain[lab] = gain;
} else {
accGain[lab] += gain;
}
}
}
auto F = [&](int lab) -> ll {
return baseSum + (visGain[lab] == stampGain ? accGain[lab] : 0LL);
};
ll dpSelf = F(a[v]);
if (par[v] != 0) up[v] = F(a[par[v]]);
// Build S_v(label) from candidates:
// 1) v keeps its own badge
// 2) v swaps with one child c, then final badge on v is a[c]
++stampBest;
vector<int> touched;
touched.reserve(children[v].size() + 1);
auto relax = [&](int lab, ll val) {
if (visBest[lab] != stampBest) {
visBest[lab] = stampBest;
bestForLabel[lab] = val;
touched.push_back(lab);
} else {
bestForLabel[lab] = max(bestForLabel[lab], val);
}
};
relax(a[v], dpSelf);
ll best = dpSelf;
for (int c : children[v]) {
int lab = a[c];
ll gainHere = max(0LL, sameSelf[c] + (ll)w[lab] - bestBase[c]);
ll gc_lab = bestBase[c] + gainHere; // g_c(a[c])
ll val = up[c] + ((a[v] == lab) ? (ll)w[lab] : 0LL) + F(lab) - gc_lab;
relax(lab, val);
best = max(best, val);
}
bestBase[v] = best;
sameSelf[v] = bestForLabel[a[v]];
states[v].clear();
states[v].reserve(touched.size());
for (int lab : touched) {
states[v].push_back({lab, bestForLabel[lab]});
}
}
cout << bestBase[1] << '\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;
int N = 2 * n;
vector<int> w(n + 1);
for (int i = 1; i <= n; ++i) cin >> w[i];
vector<int> a(N + 1);
for (int i = 1; i <= N; ++i) cin >> a[i];
vector<vector<int>> g(N + 1);
for (int i = 0; i < N - 1; ++i) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
// Root the tree at 1.
vector<int> par(N + 1, 0), order;
vector<vector<int>> children(N + 1);
order.reserve(N);
order.push_back(1);
par[1] = 0;
for (int i = 0; i < (int)order.size(); ++i) {
int v = order[i];
for (int to : g[v]) {
if (to == par[v]) continue;
par[to] = v;
children[v].push_back(to);
order.push_back(to);
}
}
// DP data.
vector<ll> bestBase(N + 1, 0); // B_v
vector<ll> up(N + 1, 0); // U_v
vector<ll> sameSelf(N + 1, 0); // S_v(a_v)
vector<vector<pair<int, ll>>> states(N + 1); // (label, S_v(label))
// Temporary arrays indexed by label 1..n.
vector<ll> accGain(n + 1, 0), bestForLabel(n + 1, 0);
vector<int> visGain(n + 1, 0), visBest(n + 1, 0);
int stampGain = 0, stampBest = 0;
for (int it = N - 1; it >= 0; --it) {
int v = order[it];
// Build F_v(L) = sum_c g_c(L) as baseSum + accumulated gains for label L.
++stampGain;
ll baseSum = 0;
for (int c : children[v]) {
baseSum += bestBase[c];
for (auto [lab, val] : states[c]) {
ll gain = val + (ll)w[lab] - bestBase[c];
if (gain <= 0) continue;
if (visGain[lab] != stampGain) {
visGain[lab] = stampGain;
accGain[lab] = gain;
} else {
accGain[lab] += gain;
}
}
}
auto F = [&](int lab) -> ll {
return baseSum + (visGain[lab] == stampGain ? accGain[lab] : 0LL);
};
ll dpSelf = F(a[v]);
if (par[v] != 0) up[v] = F(a[par[v]]);
// Build S_v(label) from candidates:
// 1) v keeps its own badge
// 2) v swaps with one child c, then final badge on v is a[c]
++stampBest;
vector<int> touched;
touched.reserve(children[v].size() + 1);
auto relax = [&](int lab, ll val) {
if (visBest[lab] != stampBest) {
visBest[lab] = stampBest;
bestForLabel[lab] = val;
touched.push_back(lab);
} else {
bestForLabel[lab] = max(bestForLabel[lab], val);
}
};
relax(a[v], dpSelf);
ll best = dpSelf;
for (int c : children[v]) {
int lab = a[c];
ll gainHere = max(0LL, sameSelf[c] + (ll)w[lab] - bestBase[c]);
ll gc_lab = bestBase[c] + gainHere; // g_c(a[c])
ll val = up[c] + ((a[v] == lab) ? (ll)w[lab] : 0LL) + F(lab) - gc_lab;
relax(lab, val);
best = max(best, val);
}
bestBase[v] = best;
sameSelf[v] = bestForLabel[a[v]];
states[v].clear();
states[v].reserve(touched.size());
for (int lab : touched) {
states[v].push_back({lab, bestForLabel[lab]});
}
}
cout << bestBase[1] << '\n';
}
return 0;
}