The 2021 ICPC Asia Shanghai Regional Programming Contest H. Life is a Game 题解 Kruskal重构树
题目链接: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;
}
浙公网安备 33010602011771号