新千题计划 2#:[AHOI && JSOI14] 骑士游戏

骑士游戏 n 种怪兽。每种怪兽普通攻击或法术攻击需若干体力。法术攻击会彻底杀死怪兽,普通攻击会使得怪兽变成另几种怪兽(可能有环,甚至可能自环)。现有一只 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;
}

猜你喜欢

转载自blog.csdn.net/qq_19982679/article/details/81662944
今日推荐