给你一个无向图,每个点有一个点权,从
u
−
>
v
u->v
u−>v的边权为
w
w
w,给定一个出发点
u
u
u,你当前积分为
k
k
k,如果想去
v
v
v,那么你手里的积分要
≥
w
\geq w
≥w,每当走到一个新的点就可以得到这个点的权值,每个权值只能得到一次,问你最后最多手里有多少分
考虑对整个图建立
k
r
u
s
k
a
l
kruskal
kruskal重构树,叶子节点就是这些点,
s
z
[
i
]
sz[i]
sz[i]表示以
i
i
i为根的树的点权和,
v
a
l
[
i
]
val[i]
val[i]表示
i
i
i通过节点的需要的最小分数。这样问题就转换成了从叶子节点到根节点所能得到的最大分数,由于
k
r
u
s
k
a
l
kruskal
kruskal重构树的性质,我们可以从叶子到根结点树上倍增,直到找到一个节点是无法通过的,统计子树和即可
#include<bits/stdc++.h>usingnamespace std;typedeflonglong ll;constint N =2e5+20;struct st{int u, v;
ll w;booloperator<(const st &B)const{return w < B.w;}}vs[N];int s[N];
ll sz[N], val[N];intFIND(int x){return x == s[x]? x : s[x]=FIND(s[x]);}int f[N][30];
ll d[N][30];
vector<int> g[N];voidDfs(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];// u到达父亲节点至少需要多少额外分数}intmain(){
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--){// 从大到小因为必须要先算出d[f[i][j - 1]][j - 1]才能取最大值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';}return0;}