常规做法是kruscal重构树。但是并查集+启发式合并也能很快的AC这题。
给定n个点的点权,m条边的边权,q个询问,每个询问是一开始在点x上,初始能量为k。如果能走到一个点,则k += 点权;如果此时的能量 >= 边权,则能经过这条边。对于每个询问,输出最大能获取的能量。
考虑每加一条边,如果连接了两个连通块 fx 与 fy,则 fx 中某一些能量就能更新,剩下的由于能量不够大所以不能更新。如果对边权从下到大排序的话,那么此时不能更新能量的点,以后肯定再也不能更新了。那么如何得知一个点的能量不能再更新呢?
对于每一个点,建立优先队列:
复制代码
1
2
3
4
5
6
7
8struct po{ int val, pos; bool operator < (const po &k) const { return val > k.val; } }; priority_queue<po> q[N];
q[i]存放的是关于起始点在i的初始能量。如果一个能量能够更新,那么必然有 val + s[fx] >= 边权。反之则不能更新,就写入ans,并且在优先队列中删除。
在q[fx]和q[fy]中删除那些不能更新的后,就该启发式合并了,比较q[fx]和q[fy]的大小,小的往大的身上合并。
一开始我的写法是:访问q[fx]和q[fy]的所有点,能更新的更新后然后放入temp队列中,不能更新的放入ans数组中,直到q[fx]和q[y]都空了,再把temp中的放回q[fy],就完成了合并的操作。这样子的错误在于,访问了所有的点,q[fy]有可能越来越大,所以复杂度达到了n2。
但是能够更新的是不必马上更新的,也就是不用去访问,所以可以启发式合并,复杂度就变为了nlogn。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76#include<bits/stdc++.h> using namespace std; #define int long long const int N = 1e5 + 5, mod = 1e9 + 7; struct node{ int x, y, val; bool operator < (const node &k) const { return val < k.val; } } a[N]; struct po{ int val, pos; bool operator < (const po &k) const { return val > k.val; } }; int fa[N], siz[N]; int s[N], ans[N]; priority_queue<po> q[N]; int getf(int k) { if (k == fa[k]) return k; return fa[k] = getf(fa[k]); } void add(int x, int val) { while(!q[x].empty()){ po w = q[x].top(); if (w.val + s[x] < val) { q[x].pop(); ans[w.pos] = w.val + s[x]; } else break; } } signed main() { int n, m, ques; cin >> n >> m >> ques; for (int i = 1; i <= n; i++) { scanf("%lld", &s[i]); fa[i] = i; siz[i] = 1; } for (int i = 1; i <= m; i++) { scanf("%lld%lld%lld", &a[i].x, &a[i].y, &a[i].val); } sort(a + 1, a + 1 + m); for (int i = 1; i <= ques; i++) { int x, k; scanf("%lld%lld", &x, &k); q[x].push(po{k, i}); } for (int i = 1; i <= m; i++){ int fx = getf(a[i].x), fy = getf(a[i].y), val = a[i].val; if (fx == fy) continue; add(fx, val); add(fy, val); if (q[fx].size() > q[fy].size()) swap(fx, fy); fa[fx] = fy; s[fy] += s[fx]; while(!q[fx].empty()) { q[fy].push(q[fx].top()); q[fx].pop(); } } int ff = getf(1); while(!q[ff].empty()) { po w = q[ff].top(); q[ff].pop(); ans[w.pos] = w.val + s[ff]; } for (int i = 1; i <= ques; i++) printf("%lldn", ans[i]); return 0; }
最后
以上就是害羞钥匙最近收集整理的关于2021 icpc 上海 H. Life is a Game的全部内容,更多相关2021内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复