题目链接:https://codeforces.com/gym/103446/problem/H
题目大意:
人生就是一场游戏。
世界可以看作是 \(n\) 个城市和城市之间 \(m\) 条无向道路的无向连通图。
现在你,生命游戏玩家,将在世界图表上玩生命游戏。最初,您位于第 \(x\) 个城市和第 \(k\) 个社交能力点。你可以通过生活和工作来获得社交能力点数。
具体来说,您可以通过在第 \(i\) 个城市生活和工作来赚取 \(a_i\) 社交能力点数。但在这个问题中,你不能在一个城市重复获得社交能力点数。所以你想环游世界,获得更多的社交能力点数。然而,道路并不容易。具体来说,第 \(i\) 条道路有一个能力阈值 \(w_i\) ,你至少要有 \(w_i\) 的社交能力点数才能通过这条路。此外,通过道路时,您的社交能力点数不会减少,但如果您想通过第 \(i\) 个道路,则至少需要 \(w_i\) 个社交能力点数奥德。正如你所看到的,生活游戏就是重复地生活、工作和旅行。有 \(q\) 个游戏保存。
每次保存游戏时,都会给出初始城市和社交能力点,并且玩家没有在任何城市生活或工作过。现在你,现实生活中的游戏玩家,需要确定在游戏结束时你可以拥有的社交能力点数的最大可能数量,并为每个给定的游戏保存输出它。
解题思路:
Kruskal重构树 基础练习题。
思路参考自官方题解:https://codeforces.com/gym/103446/attachments/download/14828/LiyuuCute.pdf
这里主要要讲一下就是怎么倍增:
在 kruskal 重构的树中,
对于当前节点 \(u\) 来说,设它的父节点是 \(p\),只有在满足 \(u\) 对应的节点权值和 \(+k\) \(\ge\) 节点 \(p\) 对应的边权时,才能从 \(u\) 走到 \(p\)。
所以本题的关键体现在代码中的 \(ff\) 数组,
ff[u][i]
表示 \(u\) 能往上(即祖先节点那个方向)走 \(2^i\) 所需的最小的 \(k\) 是多少。
这一部分是我感觉最需要思考的地方(其它感觉都还好)。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5, maxm = 2e5 + 5;int n, m, q, f[maxn];
int fa[maxn][17], tr[maxn], idx, dep[maxn], val[maxn];
vector<int> g[maxn];void init() {for (int i = 1; i < 2*n; i++) {f[i] = fa[i][0] = i;g[i].clear();}idx = n;
}int find(int x) {return x == f[x] ? x : f[x] = find(f[x]);
}struct Edge {int u, v, w;bool operator < (const Edge &b) const {return w < b.w;}
} e[maxm];void dfs(int u, int d) {dep[u] = d;for (auto v : g[u])dfs(v, d+1);
}void kruskal_build_tree() {init();sort(e, e+m);for (int i = 0; i < m; i++) {int u = e[i].u, v = e[i].v, w = e[i].w;int x = find(u), y = find(v);if (x != y) {int z = ++idx;tr[z] = w;fa[z][0] = z;g[z].push_back(x);g[z].push_back(y);fa[x][0] = fa[y][0] = z;f[x] = f[y] = z;val[z] = val[x] + val[y];}}for (int i = 1; i <= idx; i++)if (fa[i][0] == i)dfs(i, 0);for (int i = 1; i <= 16; i++)for (int u = 1; u <= idx; u++)fa[u][i] = fa[ fa[u][i-1] ][i-1];
}//int cal(int x, int k) { // 暴力cal会TLE
// while (fa[x][0] != x) {
// int p = fa[x][0];
// if (k + val[x] >= tr[p])
// x = p;
// else
// break;
// }
// return k + val[x];
//}int ff[maxn][20];
int before_cal() {for (int u = 1; u <= idx; u++) {if (fa[u][0] == u) {ff[u][0] = 2e9;}else {int p = fa[u][0];ff[u][0] = tr[p] - val[u];}}for (int i = 1; i <= 16; i++)for (int u = 1; u <= idx; u++)ff[u][i] = max(ff[u][i-1], ff[ fa[u][i-1] ][i-1]);
}int cal(int x, int k) {for (int i = 16; i >= 0; i--) {if (k >= ff[x][i]) {x = fa[x][i];}}return k + val[x];
}int main() {while (~scanf("%d%d%d", &n, &m, &q)) {for (int i = 1; i <= n; i++) {scanf("%d", val+i);}for (int i = 0; i < m; i++)scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);kruskal_build_tree();before_cal();while (q--) {int x, k;scanf("%d%d", &x, &k);int ans = cal(x, k);printf("%d\n", ans);}}return 0;
}