题目大意:给定n个点,m条边,每个点上有一个点权,每条边有一个边权;走到这个点后你的能力会增加这个点权,你的能力需要大于一条边的边权才可以走到这条边。(1<=n,m,q<=)
解题思路:很明显是先找一个最小生成树,能够以最小的边权走到每个点的预期路径。Kruskal重构树就是在Kruskal最小生成树的基础上延伸的一个算法,它是一个二叉树,原图中的点都是叶子结点,两个叶子结点的父亲是他们两之间的边权,每次选边权最小的两个点,自底向上构造,保证往上的边权非递减。任意两点间的边权是他们的LCA(当然这题可以不需要用到LCA)。最终的结点有2N个,需要开2N的空间。下图是样例的Kruskal重构树。方框的结点是虚点,结点旁边的值是需要走完它的子树能获得的能量值,这个在建树的时候就可以得到了。每次获得一个初始结点,往上跳,这个需要用倍增跳(因为最坏的情况下数据卡到,树接近单链,逐层一步步跳的时间复杂度就是O(nq)了)。如果大于上面的这个边权值,就跳到这个位置,答案等于这个点统计出来的子树能获得的能量值+初始能量值。
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const int maxN = 2010;
const int mod = 1e6 + 7;
const int INF = (1ll << 31) - 1;
const ll LNF = 1e18;
const double eps = 1e-6;
typedef pair<int, int> PII;
int p[N << 1], depth[N << 1], fa[N << 1][21];
ll sum[N << 1], a[N], val[N << 1];
int e[N << 2], ne[N << 2], h[N << 1], idx;
struct Node{
int a, b;
ll c;
bool operator<(const Node &A)const{
return c < A.c;
}
}v[N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
int find(int x) {
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
void bfs(int u) {
queue<int>qu; qu.push(u);
depth[u] = 1, depth[0] = 0;
while (!qu.empty()) {
int u = qu.front(); qu.pop();
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (depth[v] > depth[u] + 1) {
depth[v] = depth[u] + 1;
qu.push(v);
fa[v][0]= u;
for (int k = 1; k <= 20; k ++ ) {
fa[v][k] = fa[fa[v][k - 1]][k - 1];
}
}
}
}
}
void solve() {
memset(h, -1, sizeof(h));
memset(depth, 0x3f, sizeof(depth));
int n, m, q; cin >> n >> m >> q;
for (int i = 1; i <= 2 * n; i ++ ) p[i] = i;
for (int i = 1; i <= n; i ++ ) cin >> a[i], sum[i] = a[i];
for (int i = 1; i <= m; i ++ ) {
int x, y;
ll z; cin >> x >> y >> z;
v[i] = {x, y, z};
}
sort(v + 1, v + 1 + m);
int k = n;
for (int i = 1; i <= m; i ++ ) {
int x = v[i].a, y = v[i].b;
ll z = v[i].c;
x = find(x), y = find(y);
if (x == y) continue;
p[x] = p[y] = ++ k;
val[k] = z;
add(x, k), add(k, x), add(y, k), add(k, y);
sum[k] += sum[x] + sum[y];//a的集合值
}
for (int i = k; i >= 1; i -- ) if (depth[i] == 0x3f3f3f3f) bfs(i);
while (q -- ) {
ll x, ini;
cin >> x >> ini;
ll ans = ini + a[x];
while(x != k) {
int x1 = x;
for (int i = 20; i >= 0; i -- ) {
if (fa[x][i] && val[fa[x][i]] <= ans) {
x = fa[x][i];
}
}
if (x1 == x) break;
ans = sum[x] + ini;
}
cout << ans << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int _;
//cin >> _;
_ = 1;
while (_ -- ) {
solve();
}
return 0;
}