P4042 [AHOI2014/JSOI2014]骑士游戏
一共有\(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;
}
$$Life \quad is \quad fantastic!$$