骑士游戏【难度:NOIP D2T1】有 n n <script type="math/tex" id="MathJax-Element-3">n</script> 种怪兽。每种怪兽普通攻击或法术攻击需若干体力。法术攻击会彻底杀死怪兽,普通攻击会使得怪兽变成另几种怪兽(可能有环,甚至可能自环)。现有一只 1 号怪兽,输出彻底杀死怪兽的最小体力。
最短路。明显的图论问题,虽然首次看到此题时刚学完语法,然后 TLE + RE。
可以用 Dijkstra,由于每个点影响入度,更新时扫一遍逆图逐个更新。此处不能保证初次出堆已最优,因此不能只出堆一次。为避免堆内重复元素重复处理,我们记录入堆权值,若出堆元素之入堆权值非其当前最优权值,则跳过。一个节点之每个出度都会使节点更新一次,为减少更新次数,我们记录 wiat[i]
数组(wait
貌似是关键字)代表能更新节点 i
之最后节点,等到 wiat[i]
点出堆时再更新 i
。
以下代码,由于罹患 STL 依赖症,只能吸氧。手写数构会比较快。
// luogu-judger-enable-o2
#include <cstdio>
#include <utility>
#include <functional>
#include <algorithm>
#include <queue>
#include <vector>
#define Add push_back
#define Pad(a, b) push(std::make_pair(a, b))
typedef long long int LINT;
typedef std::pair<LINT, int> PLI;
const int MAXN = 200001;
int n, wiat[MAXN]; LINT s[MAXN], k[MAXN];
std::vector<int> g1[MAXN], g2[MAXN];
std::priority_queue<PLI, std::vector<PLI>, std::greater<PLI> > q;
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
int r, ip; scanf("%lld%lld%d", s + i, k + i, &r);
q.Pad(k[i], i);
while(r--) { scanf("%d", &ip); g1[i].Add(ip); g2[ip].Add(i); }
}
while(!q.empty()) {
LINT tr = q.top().first; int cur = q.top().second; q.pop();
if(tr > k[cur]) continue;
for(auto i: g2[cur]) if(!wiat[i] || wiat[i] == cur) {
LINT now = s[i]; for(auto j: g1[i]) now += k[j];
if(now < k[i]) {
k[i] = now, q.Pad(now, i);
for(auto j: g2[i]) wiat[j] = i;
}
}
}
printf("%lld\n", k[1]);
return 0;
}