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.
Fix the empty vessel . Instead of thinking about the whole sum immediately, ask: what is the largest possible value of one particular vessel ? If has height , every path from to must somehow block that height.
For fixed , the maximum possible water in vessel is the minimax path value
Upper bound: if a path has all edges below , then height would be forced all the way to the empty vessel. Lower bound: setting for all is actually good.
So the answer for vessel is just
where is the bottleneck distance in the weighted cycle. This has nothing to do with simulating water anymore. The water is gone. Good riddance.
Bottleneck distances in any undirected weighted graph are preserved by an MST: equals the maximum edge on the - path in the MST. For a cycle, an MST is obtained by deleting any edge with maximum height.
Now you only need, for every vertex of a weighted tree path, the sum of maximum edge weights on paths to all other vertices. Sort the remaining edges increasingly and use DSU. When an edge of weight joins components and , every pair across them gets bottleneck , so add to every vertex in and to every vertex in . Maintain component-wide lazy tags and merge member lists small-to-large.
Fix the vessel that must remain empty, so .
Consider another vessel . Suppose we try to make . Take any path from to whose edge heights are all strictly smaller than . Starting from , across the last edge of that path we have one endpoint with value and the edge height is below , so the condition forces the previous vessel to also have value . This propagates all the way back to , forcing , which is impossible.
Therefore every valid array satisfies
Define this minimax/bottleneck distance as
Now the nice part: setting
for every vertex is always good.
For an edge of height , the bottleneck distances satisfy
because we can go from to optimally and then cross this edge. If , then the inequality forces . Symmetrically, whenever the two endpoint values differ, their maximum is at most , exactly what the condition requires.
So for empty vessel , the answer is
with .
We reduced the entire problem to: for every vertex, sum bottleneck distances to all other vertices in a weighted cycle.
In any undirected weighted graph, bottleneck distances are preserved by a minimum spanning tree. More precisely, if is an MST, then for any , the bottleneck distance between them equals the maximum edge weight on the unique - path in .
Proof sketch: let be the maximum edge on the - path in the MST. The tree path gives a path with bottleneck , so . If there were a path with all edges , remove an edge of weight from the MST path; that creates a cut, and the alternative path crosses this cut with an edge . Replacing would make a cheaper spanning tree. Contradiction. Classic MST black magic, but legal.
For a cycle, an MST is obtained by deleting any edge with maximum height. Thus we delete one maximum edge and get a weighted path/tree with vertices and edges.
Now the answer for each vertex is:
Sort the remaining tree edges increasingly by weight.
When processing an edge of weight that connects two currently separate components and , all edges already inside those components have weight at most . Therefore for every pair
the maximum edge on their final tree path is exactly .
So this edge contributes:
The only annoying part is adding the same value to every vertex of a DSU component. Doing it naively is quadratic, and quadratic is where submissions go to die.
For each DSU root, store:
The real current answer of a vertex is
To add Delta to a whole component, just do:
When merging component into component , every vertex must keep the same real value, but its root tag changes from to . So update:
Then move into 's member list.
Always merge the smaller member list into the larger one. Each vertex moves times, so this part is .
For each test case:
Over all test cases:
which easily fits the limits.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Edge {
int u, v;
ll w;
};
struct DSU {
vector<int> parent, sz;
vector<ll> tag, base;
vector<vector<int>> members;
DSU(int n = 0) {
init(n);
}
void init(int n) {
parent.resize(n);
sz.assign(n, 1);
tag.assign(n, 0);
base.assign(n, 0);
members.clear();
members.resize(n);
iota(parent.begin(), parent.end(), 0);
for (int i = 0; i < n; i++) members[i].push_back(i);
}
int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
void unite(int u, int v, ll w) {
int a = find(u);
int b = find(v);
if (a == b) return;
tag[a] += w * sz[b];
tag[b] += w * sz[a];
if (members[a].size() < members[b].size()) swap(a, b);
ll shift = tag[b] - tag[a];
for (int x : members[b]) {
base[x] += shift;
members[a].push_back(x);
}
members[b].clear();
parent[b] = a;
sz[a] += sz[b];
}
vector<ll> values() {
int n = (int)parent.size();
vector<ll> res(n);
for (int i = 0; i < n; i++) {
res[i] = base[i] + tag[find(i)];
}
return res;
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<ll> h(n);
int removed = 0;
for (int i = 0; i < n; i++) {
cin >> h[i];
if (h[i] > h[removed]) removed = i;
}
vector<Edge> edges;
edges.reserve(n - 1);
for (int i = 0; i < n; i++) {
if (i == removed) continue;
edges.push_back({i, (i + 1) % n, h[i]});
}
sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) {
return a.w < b.w;
});
DSU dsu(n);
for (const auto& e : edges) {
dsu.unite(e.u, e.v, e.w);
}
vector<ll> ans = dsu.values();
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\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 u, v;
ll w;
};
struct DSU {
vector<int> parent, sz;
vector<ll> tag, base;
vector<vector<int>> members;
DSU(int n = 0) {
init(n);
}
void init(int n) {
parent.resize(n);
sz.assign(n, 1);
tag.assign(n, 0);
base.assign(n, 0);
members.clear();
members.resize(n);
iota(parent.begin(), parent.end(), 0);
for (int i = 0; i < n; i++) members[i].push_back(i);
}
int find(int x) {
if (parent[x] == x) return x;
return parent[x] = find(parent[x]);
}
void unite(int u, int v, ll w) {
int a = find(u);
int b = find(v);
if (a == b) return;
tag[a] += w * sz[b];
tag[b] += w * sz[a];
if (members[a].size() < members[b].size()) swap(a, b);
ll shift = tag[b] - tag[a];
for (int x : members[b]) {
base[x] += shift;
members[a].push_back(x);
}
members[b].clear();
parent[b] = a;
sz[a] += sz[b];
}
vector<ll> values() {
int n = (int)parent.size();
vector<ll> res(n);
for (int i = 0; i < n; i++) {
res[i] = base[i] + tag[find(i)];
}
return res;
}
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<ll> h(n);
int removed = 0;
for (int i = 0; i < n; i++) {
cin >> h[i];
if (h[i] > h[removed]) removed = i;
}
vector<Edge> edges;
edges.reserve(n - 1);
for (int i = 0; i < n; i++) {
if (i == removed) continue;
edges.push_back({i, (i + 1) % n, h[i]});
}
sort(edges.begin(), edges.end(), [](const Edge& a, const Edge& b) {
return a.w < b.w;
});
DSU dsu(n);
for (const auto& e : edges) {
dsu.unite(e.u, e.v, e.w);
}
vector<ll> ans = dsu.values();
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
return 0;
}