- 给你一个无向图,每个点有一个点权,从u−>vu->vu−>v的边权为www,给定一个出发点uuu,你当前积分为kkk,如果想去vvv,那么你手里的积分要≥w\geq w≥w,每当走到一个新的点就可以得到这个点的权值,每个权值只能得到一次,问你最后最多手里有多少分
- 考虑对整个图建立kruskalkruskalkruskal重构树,叶子节点就是这些点,sz[i]sz[i]sz[i]表示以iii为根的树的点权和,val[i]val[i]val[i]表示iii通过节点的需要的最小分数。这样问题就转换成了从叶子节点到根节点所能得到的最大分数,由于kruskalkruskalkruskal重构树的性质,我们可以从叶子到根结点树上倍增,直到找到一个节点是无法通过的,统计子树和即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 20;
struct st{
int u, v;
ll w;
bool operator < (const st &B)const{
return w < B.w;
}
}vs[N];
int s[N];
ll sz[N], val[N];
int FIND(int x){
return x == s[x] ? x : s[x] = FIND(s[x]);
}
int f[N][30];
ll d[N][30];
vector<int> g[N];
void Dfs(int u){
for(auto v : g[u]){
f[v][0] = u;
Dfs(v);
sz[u] += sz[v];
}
d[u][0] = val[f[u][0]] - sz[u];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, q;
cin >> n >> m >> q;
iota(s, s + N, 0);
memset(val, 0x3f, sizeof val);
memset(d, 0x3f, sizeof d);
for(int i=1;i<=n;i++) cin >> sz[i];
for(int i=0;i<m;i++){
cin >> vs[i].u >> vs[i].v >> vs[i].w;
}sort(vs, vs + m);
for(int i=0;i<m;i++){
int u = FIND(vs[i].u);
int v = FIND(vs[i].v);
int w = vs[i].w;
if(u == v) continue;
n += 1;
s[u] = s[v] = n;
g[n].push_back(v);
g[n].push_back(u);
val[n] = w;
}
Dfs(n);
for(int i=n;i>=1;i--){
for(int j=1;j<=20;j++){
f[i][j] = f[f[i][j - 1]][j - 1];
d[i][j] = max(d[i][j - 1], d[f[i][j - 1]][j - 1]);
}
}
while(q--){
int x;
ll k;
cin >> x >> k;
if(d[x][0] > k){
cout << k + sz[x] << '\n';
continue;
}
for(int i=20;i>=0;i--){
if(k >= d[x][i]){
x = f[x][i];
}
}
cout << k + sz[x] << '\n';
}
return 0;
}