P4042 [AHOI2014/JSOI2014]骑士游戏

$$\large\text{要用魔法打败魔法}$$

P4042

一共有\(n\)种不同的怪物,每种怪物都可以用两种方式杀死,第一种消耗\(s_i\)体力将怪物变成另外一种或多种怪物,第二种消耗\(k_i\)体力,可以直接杀死,问最少消耗体力杀死\(1\)号怪物

读完题之后感觉是个图的模型,但是建了一下图发现不太好建边,更不太好跑最短路。(淦

那就从\(dp\)角度来思考一下。

\(f[i]\)杀死第\(i\)号怪需要的最小体力,显然有\(f[i]=\min(k_i,s_i+\sum\limits_{j=1}^{R_i}f[r_{i,j}])\)\(r_{i,j}\)表示用蛮力杀死\(i\)后分裂的第\(j\)个。

经过了上面的建图过程就会发现,可能会存在环(也就是有后效性),到时候就死了。(淦\(\times2\)

现在大概只能在\(dp\)的式子上下手了,显然后效性在\(dp\)式子后面的一项上。

当且仅当\(\forall f[r_{i,j}]<f[i]\)\(f[i]\)才能被后面一项更新,我们不妨换一个角度来进行转移,用某一个\(f\)值去更新其他的\(f\)

一个更显然的性质,对于\(k_i\)最小的怪物,\(f[i]=k_i\),所以我们对\(f\)建立一个堆,然后每次弹出一个点\(i\)来更新其他的\(f_{r_{i,j}}\),如果更新完了,则就把\(f_{r_{i,j}}\)放到堆里(这样就确保了不会有后效性)

/**
 *@ author:pyyyyyy/guhl37
 *@ bolg:https://www.cnblogs.com/pyyyyyy
 *@ Debug:数组开小了 
**/
#include <bits/stdc++.h>
#include <algorithm>
#include <queue>
#define int long long
using namespace std;
const int N = 500111;
struct Dp {
	int id, w;
	bool operator < (const Dp &y) const {
		return w > y.w;
	}
};
priority_queue<Dp> q;
struct Edge {
	int v, Next;
} e[N << 1];
int head[N], cnt;
void add(int u, int v) {
	e[++cnt].Next = head[u];
	e[cnt].v = v;
	head[u] = cnt;
}
int R[N], K[N], s[N], f[N], vis[N], n, ans[N];
signed main() {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin >> n;
	for(int i = 1; i <= n; ++i) {
		scanf("%lld %lld %lld",&s[i], &K[i], &R[i]);
		for(int j = 1, x; j <= R[i]; ++j)
			scanf("%lld",&x),add(x, i);
		q.push((Dp) {i, K[i]});
		f[i] = s[i];
	}
	while(!q.empty())
	{
		int u = q.top().id, w = q.top().w;
		q.pop();
		if(vis[u]) continue ;
		vis[u] = 1; ans[u] = w;
		for(int i = head[u]; i; i = e[i].Next){
			int to = e[i].v;
			if(vis[to] || f[to] > K[to]) continue;
			R[to]--, f[to] += w;
			if(!R[to]) q.push((Dp) {to, f[to]});
		}
	}
	cout<<ans[1];
	return 0;
}
posted @ 2020-07-07 10:47  pyyyyyy  阅读(123)  评论(0编辑  收藏  举报