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.
Only edges touching the repainted vertex can change. Everything else in the tree is completely unaffected, so don't even think about recomputing the whole answer.
Maintain the current total answer. For repainting vertex from color to , the answer changes by looking at neighbors of : edges to color may turn from free to paid, and edges to color may turn from paid to free.
Let be the sum of weights of edges from to neighbors currently having color . If , then
That one line is basically the problem. The rest is making it fast enough.
Small-degree vertices can be handled by directly scanning their adjacency list. High-degree vertices need precomputed maps: for each heavy vertex , store sum[h][color] = total edge weight from to neighbors of that color.
When any vertex changes color, every heavy neighbor's map must be updated, because that heavy vertex now has one adjacent edge moving from old color bucket to new color bucket. Store, for each vertex, the list of adjacent heavy vertices so each repaint updates only those maps.
We need support vertex recolors on a weighted tree and print the sum of edge costs after every query.
An edge contributes its weight if its endpoints have different colors, otherwise .
The dumb update is: repaint , scan all neighbors of , fix the affected edge costs. That is correct, because only edges incident to can change. But if is a star center with degree , doing that for every query is very dead, very quickly.
So this is a degree-splitting problem.
The local formula
Let the old color of be , and the new color be .
For a neighbor connected by weight :
So define:
Then, if :
If , the delta is .
That formula is the whole trick. The annoying part is answering quickly.
Small vs heavy vertices
Pick a threshold , around works fine for .
A vertex is heavy if its degree is at least .
For a light vertex, just scan all its neighbors to compute and . Since its degree is small, this is cheap.
For a heavy vertex, scanning neighbors every query is too expensive. So for every heavy vertex , maintain a hash map:
Then is just mp[h][col].
Keeping heavy maps correct
Suppose a vertex changes from to .
Any heavy vertex adjacent to has one neighbor whose color changed. If the edge weight is , then in 's map:
So for each vertex , store the list of adjacent heavy vertices, with the edge weight. After repainting , update exactly those heavy maps.
This is the part people often miss. It is not enough to update the answer; future queries involving heavy vertices need their cached color sums to reflect the new colors. Cached data that lies to you is worse than no cache. Classic footgun.
Algorithm
For each test case:
color -> total adjacent edge weight.heavyAdj[v], the adjacent heavy vertices and edge weights.(v, x):
old = color[v]sameOld = S(v, old) and sameNew = S(v, x):
v is heavy, use its mapans += sameOld - sameNewvcolor[v] = xansComplexity
Let be the threshold.
With and total , this easily fits.
Memory is , where ranges over heavy vertices. Since there are few heavy vertices, this is fine.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Edge {
int to;
ll w;
};
int main() {
setIO();
int T;
cin >> T;
const int B = 450;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> color(n + 1);
for (int i = 1; i <= n; i++) cin >> color[i];
vector<vector<Edge>> g(n + 1);
ll ans = 0;
for (int i = 0; i < n - 1; i++) {
int u, v;
ll c;
cin >> u >> v >> c;
g[u].push_back({v, c});
g[v].push_back({u, c});
if (color[u] != color[v]) ans += c;
}
vector<int> heavyId(n + 1, -1), heavy;
for (int v = 1; v <= n; v++) {
if ((int)g[v].size() >= B) {
heavyId[v] = (int)heavy.size();
heavy.push_back(v);
}
}
vector<unordered_map<int, ll>> sumByColor(heavy.size());
for (int id = 0; id < (int)heavy.size(); id++) {
int v = heavy[id];
sumByColor[id].reserve(g[v].size() * 2 + 1);
for (auto [to, w] : g[v]) {
sumByColor[id][color[to]] += w;
}
}
vector<vector<pair<int, ll>>> heavyAdj(n + 1);
for (int v = 1; v <= n; v++) {
for (auto [to, w] : g[v]) {
if (heavyId[to] != -1) heavyAdj[v].push_back({heavyId[to], w});
}
}
auto getSum = [&](int v, int col) -> ll {
int id = heavyId[v];
if (id != -1) {
auto it = sumByColor[id].find(col);
return it == sumByColor[id].end() ? 0LL : it->second;
}
ll res = 0;
for (auto [to, w] : g[v]) {
if (color[to] == col) res += w;
}
return res;
};
while (q--) {
int v, x;
cin >> v >> x;
int old = color[v];
if (old != x) {
ans += getSum(v, old) - getSum(v, x);
for (auto [id, w] : heavyAdj[v]) {
sumByColor[id][old] -= w;
sumByColor[id][x] += w;
}
color[v] = x;
}
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);
}
struct Edge {
int to;
ll w;
};
int main() {
setIO();
int T;
cin >> T;
const int B = 450;
while (T--) {
int n, q;
cin >> n >> q;
vector<int> color(n + 1);
for (int i = 1; i <= n; i++) cin >> color[i];
vector<vector<Edge>> g(n + 1);
ll ans = 0;
for (int i = 0; i < n - 1; i++) {
int u, v;
ll c;
cin >> u >> v >> c;
g[u].push_back({v, c});
g[v].push_back({u, c});
if (color[u] != color[v]) ans += c;
}
vector<int> heavyId(n + 1, -1), heavy;
for (int v = 1; v <= n; v++) {
if ((int)g[v].size() >= B) {
heavyId[v] = (int)heavy.size();
heavy.push_back(v);
}
}
vector<unordered_map<int, ll>> sumByColor(heavy.size());
for (int id = 0; id < (int)heavy.size(); id++) {
int v = heavy[id];
sumByColor[id].reserve(g[v].size() * 2 + 1);
for (auto [to, w] : g[v]) {
sumByColor[id][color[to]] += w;
}
}
vector<vector<pair<int, ll>>> heavyAdj(n + 1);
for (int v = 1; v <= n; v++) {
for (auto [to, w] : g[v]) {
if (heavyId[to] != -1) heavyAdj[v].push_back({heavyId[to], w});
}
}
auto getSum = [&](int v, int col) -> ll {
int id = heavyId[v];
if (id != -1) {
auto it = sumByColor[id].find(col);
return it == sumByColor[id].end() ? 0LL : it->second;
}
ll res = 0;
for (auto [to, w] : g[v]) {
if (color[to] == col) res += w;
}
return res;
};
while (q--) {
int v, x;
cin >> v >> x;
int old = color[v];
if (old != x) {
ans += getSum(v, old) - getSum(v, x);
for (auto [id, w] : heavyAdj[v]) {
sumByColor[id][old] -= w;
sumByColor[id][x] += w;
}
color[v] = x;
}
cout << ans << '\n';
}
}
return 0;
}