「AHOI2014/JSOI2014」骑士游戏
传送门
考虑 ( ext{DP})。
设 (dp_i) 表示灭种(雾)一只编号为 (i) 的怪物的代价。
那么转移显然是:
[dp_i = min(K_i, S_i + sum_{j = 1}^{R_i} dp_{v_j})
]
但是我们会发现这个东西是有后效性的。。。
所以我们会想要用建图然后跑一个最短路什么的来搞。。。
于是我们观察到上面那个 ( ext{DP}) 式子中,(dp_i) 如果用后面那一项来转移,显然会有 (dp_{v_j} < dp_i)。
这提示我们,为了消除后效性,可以对 (dp) 值排序。
准确的说就是开一个堆来搞,每个点初始的 (dp) 值都是消灭它的魔法消耗,然后优先更新较小的 (dp) 值,
毕竟我们对于魔法消耗最小的怪物肯定是直接消灭(因为你到头来都要干死它何必生出一些魔法消耗更高的嘞)
然后我们建图方式就是反着来,如果 (i) 会生出 (j),那么连边 (j o i),然后我们就跑一个长的有点像 ( ext{Dijkstra}) 的 ( ext{DP}) 就好了。
参考代码:
#include <cstdio>
#include <queue>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
typedef long long LL;
const int _ = 2e5 + 5, __ = 1e6 + 5;
int tot, head[_]; struct Edge { int ver, nxt; } edge[__];
inline void Add_edge(int u, int v) { edge[++tot] = (Edge) { v, head[u] }, head[u] = tot; }
int n, r[_], vis[_]; LL dp[_], s[_], k[_];
struct node { int u; LL val; } ;
inline bool operator < (const node& x, const node& y) { return x.val > y.val; }
priority_queue < node > Q;
int main() {
#ifndef ONLINE_JUDGE
file("cpp");
#endif
read(n);
for (rg int i = 1; i <= n; ++i) {
read(s[i]), read(k[i]), read(r[i]);
for (rg int x, o = 1; o <= r[i]; ++o) read(x), Add_edge(x, i);
}
for (rg int i = 1; i <= n; ++i) Q.push((node) { i, dp[i] = k[i] });
while (!Q.empty()) {
int u = Q.top().u; Q.pop();
if (vis[u]) continue ; vis[u] = 1;
for (rg int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].ver;
s[v] += dp[u];
if (dp[v] > s[v] && !--r[v])
dp[v] = s[v], Q.push((node) { v, dp[v] });
}
}
printf("%lld
", dp[1]);
return 0;
}