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.
After one operation, ask the stupid-but-deadly question: where can positive values still live? For , every non-leaf is emptied, so all remaining positive mass is sitting on original leaves. That observation nukes most of the “dynamic” part.
Only the first chosen root really changes the distribution. After that, you are just picking leaf piles one by one. So for a first root , the score is
For a fixed root and vertex , let be the neighbor of on the path to . Then sends its value to the best leaf in the component of containing : maximum distance from , tie by minimum index.
Now reroot across one edge . For every vertex except and , the first edge on the path toward the root is unchanged. Translation: only two vertex contributions and two candidate buckets need updating. This is the whole trick; everything else is bookkeeping hell.
Root the tree at . Compute for every vertex the best and second-best downward destination leaves, plus the best destination through the parent side. Maintain bucket sums and the sum of the largest active buckets with two sets. During reroot : remove ’s contribution, swap active bucket out and in, add ’s new contribution, then evaluate .
The first operation is the only operation that can move positive mass in an interesting way. After rooting at some , we collect , and then every non-leaf sends its current value to some leaf and becomes .
So after the first operation, all remaining positive values are on original leaves. In later operations:
Therefore, if the first root is , the answer for that is
where is the pile ending at vertex after the first operation, and means the sum of the largest buckets. We keep buckets for all vertices except the current root; non-leaf buckets are just , which nicely represents wasting extra operations when needed.
If , none of this matters: just output .
Fix the first root . For a vertex , let be the neighbor of on the path from to . The subtree of is exactly the component containing after deleting edge .
So sends its value to the best leaf in that component, where “best” means:
Maximum distance first, smallest index as the tie-breaker. No magic. Just tree directions.
Root the tree initially at . Suppose the current root is , and we move it across an edge to its child in this fixed DFS tree.
For any vertex , the first edge on the path from to the root does not change. Vertices inside 's subtree still look upward through the same edge; vertices outside still look toward through the same edge. Thus their destination leaves are unchanged.
Only these two contributions change:
Also, the active bucket set changes: bucket is no longer selectable because is the root, while bucket becomes selectable.
That gives constant many updates per edge. Finally, a clean 2700 problem pretending to be a swamp.
In the DFS tree rooted at , compute two downward candidates for every vertex :
Each child contributes only its own , and we also add the candidate . That self-candidate is not a bug: if there is any real leaf in the component, it has positive distance and beats ; if not, the component is just , so really is a leaf in that rooted view.
Then compute , the best destination reachable through the parent side. For a child of , the best destination for when the root moves into is
Then
This gives every destination needed during rerooting.
Maintain , the current pile assigned to vertex . We need the sum of the largest
active buckets after every reroot.
Use two ordered sets:
top: the current largest active buckets;rest: all other active buckets.Maintain
Each bucket update, insertion, or deletion costs .
For rerooting from parent to child :
Undo the same operations after returning from the child.
The preprocessing is , and every edge causes set updates, so the total complexity is
per test case, with memory.
Reference checked: Codeforces editorial/code.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Best {
int leaf;
int dist;
};
const int NEG = -1000000000;
bool betterBest(const Best &a, const Best &b) {
if (a.dist != b.dist) return a.dist > b.dist;
if (a.leaf == 0) return false;
if (b.leaf == 0) return true;
return a.leaf < b.leaf;
}
void addBest(Best x, Best &first, Best &second) {
if (betterBest(x, first)) {
second = first;
first = x;
} else if (betterBest(x, second)) {
second = x;
}
}
struct TopK {
int need = 0;
vector<ll> *val = nullptr;
vector<char> active;
set<pair<ll, int>> top, rest;
ll sum = 0;
TopK() = default;
TopK(int n, int k, vector<ll> &bucket) {
need = k;
val = &bucket;
active.assign(n + 1, 0);
}
void rebalance() {
while ((int)top.size() > need) {
auto it = top.begin();
auto p = *it;
top.erase(it);
sum -= p.first;
rest.insert(p);
}
while ((int)top.size() < need && !rest.empty()) {
auto it = prev(rest.end());
auto p = *it;
rest.erase(it);
top.insert(p);
sum += p.first;
}
while (!top.empty() && !rest.empty()) {
auto itSmall = top.begin();
auto itBig = prev(rest.end());
if (!(*itSmall < *itBig)) break;
auto small = *itSmall;
auto big = *itBig;
top.erase(itSmall);
rest.erase(itBig);
sum += big.first - small.first;
top.insert(big);
rest.insert(small);
}
}
void insertActivePair(int x) {
pair<ll, int> p = {(*val)[x], x};
top.insert(p);
sum += p.first;
rebalance();
}
void eraseActivePair(int x) {
pair<ll, int> p = {(*val)[x], x};
auto it = top.find(p);
if (it != top.end()) {
sum -= p.first;
top.erase(it);
} else {
it = rest.find(p);
if (it != rest.end()) rest.erase(it);
}
rebalance();
}
void addCandidate(int x) {
active[x] = 1;
insertActivePair(x);
}
void removeCandidate(int x) {
eraseActivePair(x);
active[x] = 0;
}
void changeBucket(int x, ll delta) {
if (x == 0) return;
if (active[x]) eraseActivePair(x);
(*val)[x] += delta;
if (active[x]) insertActivePair(x);
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<ll> a(n + 1);
ll maxA = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
maxA = max(maxA, 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);
}
if (k == 1) {
cout << maxA << '\n';
continue;
}
vector<int> parent(n + 1, 0), order;
order.reserve(n);
parent[1] = -1;
vector<int> st = {1};
while (!st.empty()) {
int v = st.back();
st.pop_back();
order.push_back(v);
for (int to : g[v]) {
if (to == parent[v]) continue;
parent[to] = v;
st.push_back(to);
}
}
vector<Best> down1(n + 1), down2(n + 1), up(n + 1);
for (int idx = n - 1; idx >= 0; idx--) {
int v = order[idx];
down1[v] = {v, 0};
down2[v] = {0, NEG};
for (int to : g[v]) {
if (parent[to] != v) continue;
Best cand = {down1[to].leaf, down1[to].dist + 1};
addBest(cand, down1[v], down2[v]);
}
}
vector<int> parentDest(n + 1, 0);
up[1] = {0, NEG};
for (int v : order) {
for (int to : g[v]) {
if (parent[to] != v) continue;
Best inside = (down1[v].leaf == down1[to].leaf ? down2[v] : down1[v]);
Best best = inside;
if (betterBest(up[v], best)) best = up[v];
parentDest[to] = best.leaf;
up[to] = {best.leaf, best.dist + 1};
}
}
vector<ll> bucket(n + 1, 0);
vector<int> dest(n + 1, 0);
for (int v = 2; v <= n; v++) {
dest[v] = down1[v].leaf;
bucket[dest[v]] += a[v];
}
TopK ds(n, k - 1, bucket);
for (int v = 2; v <= n; v++) ds.addCandidate(v);
ll answer = 0;
auto moveRoot = [&](int p, int v) {
ds.changeBucket(dest[v], -a[v]);
dest[v] = 0;
ds.removeCandidate(v);
ds.addCandidate(p);
dest[p] = parentDest[v];
ds.changeBucket(dest[p], a[p]);
};
auto undoMove = [&](int p, int v) {
ds.changeBucket(dest[p], -a[p]);
dest[p] = 0;
ds.removeCandidate(p);
ds.addCandidate(v);
dest[v] = down1[v].leaf;
ds.changeBucket(dest[v], a[v]);
};
struct Event {
int type;
int p;
int v;
};
vector<Event> events;
events.push_back({0, 0, 1});
while (!events.empty()) {
Event cur = events.back();
events.pop_back();
if (cur.type == 0) {
int v = cur.v;
answer = max(answer, a[v] + ds.sum);
for (int to : g[v]) {
if (parent[to] == v) events.push_back({1, v, to});
}
} else if (cur.type == 1) {
moveRoot(cur.p, cur.v);
events.push_back({2, cur.p, cur.v});
events.push_back({0, cur.p, cur.v});
} else {
undoMove(cur.p, cur.v);
}
}
cout << answer << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Best {
int leaf;
int dist;
};
const int NEG = -1000000000;
bool betterBest(const Best &a, const Best &b) {
if (a.dist != b.dist) return a.dist > b.dist;
if (a.leaf == 0) return false;
if (b.leaf == 0) return true;
return a.leaf < b.leaf;
}
void addBest(Best x, Best &first, Best &second) {
if (betterBest(x, first)) {
second = first;
first = x;
} else if (betterBest(x, second)) {
second = x;
}
}
struct TopK {
int need = 0;
vector<ll> *val = nullptr;
vector<char> active;
set<pair<ll, int>> top, rest;
ll sum = 0;
TopK() = default;
TopK(int n, int k, vector<ll> &bucket) {
need = k;
val = &bucket;
active.assign(n + 1, 0);
}
void rebalance() {
while ((int)top.size() > need) {
auto it = top.begin();
auto p = *it;
top.erase(it);
sum -= p.first;
rest.insert(p);
}
while ((int)top.size() < need && !rest.empty()) {
auto it = prev(rest.end());
auto p = *it;
rest.erase(it);
top.insert(p);
sum += p.first;
}
while (!top.empty() && !rest.empty()) {
auto itSmall = top.begin();
auto itBig = prev(rest.end());
if (!(*itSmall < *itBig)) break;
auto small = *itSmall;
auto big = *itBig;
top.erase(itSmall);
rest.erase(itBig);
sum += big.first - small.first;
top.insert(big);
rest.insert(small);
}
}
void insertActivePair(int x) {
pair<ll, int> p = {(*val)[x], x};
top.insert(p);
sum += p.first;
rebalance();
}
void eraseActivePair(int x) {
pair<ll, int> p = {(*val)[x], x};
auto it = top.find(p);
if (it != top.end()) {
sum -= p.first;
top.erase(it);
} else {
it = rest.find(p);
if (it != rest.end()) rest.erase(it);
}
rebalance();
}
void addCandidate(int x) {
active[x] = 1;
insertActivePair(x);
}
void removeCandidate(int x) {
eraseActivePair(x);
active[x] = 0;
}
void changeBucket(int x, ll delta) {
if (x == 0) return;
if (active[x]) eraseActivePair(x);
(*val)[x] += delta;
if (active[x]) insertActivePair(x);
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<ll> a(n + 1);
ll maxA = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
maxA = max(maxA, 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);
}
if (k == 1) {
cout << maxA << '\n';
continue;
}
vector<int> parent(n + 1, 0), order;
order.reserve(n);
parent[1] = -1;
vector<int> st = {1};
while (!st.empty()) {
int v = st.back();
st.pop_back();
order.push_back(v);
for (int to : g[v]) {
if (to == parent[v]) continue;
parent[to] = v;
st.push_back(to);
}
}
vector<Best> down1(n + 1), down2(n + 1), up(n + 1);
for (int idx = n - 1; idx >= 0; idx--) {
int v = order[idx];
down1[v] = {v, 0};
down2[v] = {0, NEG};
for (int to : g[v]) {
if (parent[to] != v) continue;
Best cand = {down1[to].leaf, down1[to].dist + 1};
addBest(cand, down1[v], down2[v]);
}
}
vector<int> parentDest(n + 1, 0);
up[1] = {0, NEG};
for (int v : order) {
for (int to : g[v]) {
if (parent[to] != v) continue;
Best inside = (down1[v].leaf == down1[to].leaf ? down2[v] : down1[v]);
Best best = inside;
if (betterBest(up[v], best)) best = up[v];
parentDest[to] = best.leaf;
up[to] = {best.leaf, best.dist + 1};
}
}
vector<ll> bucket(n + 1, 0);
vector<int> dest(n + 1, 0);
for (int v = 2; v <= n; v++) {
dest[v] = down1[v].leaf;
bucket[dest[v]] += a[v];
}
TopK ds(n, k - 1, bucket);
for (int v = 2; v <= n; v++) ds.addCandidate(v);
ll answer = 0;
auto moveRoot = [&](int p, int v) {
ds.changeBucket(dest[v], -a[v]);
dest[v] = 0;
ds.removeCandidate(v);
ds.addCandidate(p);
dest[p] = parentDest[v];
ds.changeBucket(dest[p], a[p]);
};
auto undoMove = [&](int p, int v) {
ds.changeBucket(dest[p], -a[p]);
dest[p] = 0;
ds.removeCandidate(p);
ds.addCandidate(v);
dest[v] = down1[v].leaf;
ds.changeBucket(dest[v], a[v]);
};
struct Event {
int type;
int p;
int v;
};
vector<Event> events;
events.push_back({0, 0, 1});
while (!events.empty()) {
Event cur = events.back();
events.pop_back();
if (cur.type == 0) {
int v = cur.v;
answer = max(answer, a[v] + ds.sum);
for (int to : g[v]) {
if (parent[to] == v) events.push_back({1, v, to});
}
} else if (cur.type == 1) {
moveRoot(cur.p, cur.v);
events.push_back({2, cur.p, cur.v});
events.push_back({0, cur.p, cur.v});
} else {
undoMove(cur.p, cur.v);
}
}
cout << answer << '\n';
}
return 0;
}