2021 上海ICPC Life is a Game(kruskal重构树,倍增)
链接
题意:给出一个n个点m条边的无向图,每个点有点权,每条边有边权,第一次到达一个点可以获得这个点的点权,经过一条边需要当前总价值大于等于边权。对于每次询问,给出起点x和初始价值k,问这次能获得的最大权值是多大
思路:对于所有路径首先选择的边一定是最小生成树上的边,然后利用kruskal重构树的性质,详情见oi-wiki,建好新树后利用倍增处理好一个点往上的父节点和最大
w
i
w_i
wi,方法类似LCA倍增,然后对于一个询问,不断往上跳,最后输出
k
+
a
[
x
]
k+a[x]
k+a[x],
a
[
i
]
a[i]
a[i]代表到
i
i
i点的所有权值和,因为一定可以获取这个点往下的所有权值。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;
struct Edge1{int u, v, d;}e1[N];
int head[N << 3], idx, a[N << 1];
struct Edge{int to, nxt;}e[N << 3];
void add(int u, int v) {e[++idx].to = v, e[idx].nxt = head[u], head[u] = idx;}
bool cmp(Edge1 _a, Edge1 b) {return _a.d < b.d;}
int f[N << 1];
int find(int n) {return n == f[n] ? n : f[n] = find(f[n]);}
int n, m, q;
int wi[N << 1], fa[N << 1][33], w[N << 1][33];
void dfs(int u, int _f)
{
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v == _f) continue;
dfs(v, u);
a[u] += a[v];
}fa[u][0] = _f, w[u][0] = wi[_f] - a[u];
}
void kruskal()
{
sort(e1 + 1, e1 + 1 + m, cmp);
int cnt = n;
for (int i = 1; i <= m; i++) {
int x = e1[i].u, y = e1[i].v, d = e1[i].d;
x = find(x), y = find(y);
if (x == y) continue;
cnt++;
f[x] = cnt, f[y] = cnt;
add(cnt, x); add(cnt, y); wi[cnt] = d;
}
dfs(cnt, 0);
for (int i = 1; i <= 25; i++) {
for (int j = 1; j <= cnt; j++) {
fa[j][i] = fa[fa[j][i - 1]][i - 1];
w[j][i] = max(w[j][i - 1], w[fa[j][i - 1]][i - 1]);
}
}
}
signed main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n + m; i++) f[i] = i;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) {
cin >> e1[i].u >> e1[i].v >> e1[i].d;
}
kruskal();
while (q--) {
int x, k;
cin >> x >> k;
for (int i = 24; i >= 0; i--) {
if (fa[x][i]) {
if (w[x][i] <= k) {x = fa[x][i];}
}
}cout << k + a[x] << endl;
}
return 0;
}