题目链接:https://ac.nowcoder.com/acm/contest/24872/H
分析
利用kruskal重构树,倍增法向上找到能够走到的最远点,再维护一个子树和即可。
代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn = 2e5+5;
const int mod = 1e9+7;
using namespace std;
ll sz[maxn], val[maxn];
int par[maxn], f[21][maxn], n, m, cnt;
vector<int> G[maxn];
vector< tuple<int, int, int> > es;
int find(int x) {
if (par[x] == x) return x;
return par[x] = find(par[x]);
}
void kruskal() {
cnt = n;
sort(es.begin(), es.end());
for(int i = 0; i < m; i++) {
auto [w, u, v] = es[i];
u = find(u), v = find(v);
if (u != v) {
val[++ cnt] = w;
par[cnt] = par[u] = par[v] = cnt;
G[u].emplace_back(cnt);
G[v].emplace_back(cnt);
G[cnt].emplace_back(u);
G[cnt].emplace_back(v);
}
}
}
void dfs0(int u, int fa) {
f[0][u] = fa;
for(int i = 1; i <= 20; i++) {
f[i][u] = f[i - 1][f[i - 1][u]];
}
for(int v : G[u]) {
if (v == fa) continue;
dfs0(v, u);
sz[u] += sz[v];
}
}
int main() {
int q;
cin >> n >> m >> q;
for(int i=1;i<=n;i++) par[i] = i;
for(int i = 1; i <= n; i++) {
cin >> sz[i];
}
for(int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
es.emplace_back(w, u, v);
}
kruskal();
dfs0(cnt, 0);
val[0] = 1e18 + 7;
while(q --) {
int u;
ll s;
cin >> u >> s;
ll cur = s + sz[u];
while(u != cnt) {
int x = u;
for(int i = 20; i >= 0; i--) {
if (val[f[i][u]] <= cur) u = f[i][u];
}
if (x == u) break;
cur = sz[u] + s;
}
cout << cur << '\n';
}
return 0;
}