AOJ 0553 - Dungeon
問題文 : ダンジョン
解法 :
各階で回復出来る量を記録しながら, 実際にダンジョンを潜っていく.
体力が負になってしまった場合, 最も回復出来る地点で回復する.
体力をセグメント木として持っておき, 区間に加算するクエリと区間の最大値を求めるクエリを実装するとうまく上記の操作ができる.
コード :
#include <cstdio> #include <queue> #include <algorithm> using namespace std; typedef long long ll; typedef struct { int plus, pos; } Dungeon; bool operator < (const Dungeon &a, const Dungeon &b) { if (a.plus != b.plus){ return (a.plus < b.plus); } else { return (a.pos < b.pos); } } int data[1 << 18], datb[1 << 18]; int getMax(int a, int b, int k, int l, int r) { if (b <= l || r <= a) return (0); if (a <= l && r <= b){ return (data[k] + datb[k]); } int lch = getMax(a, b, k * 2 + 1, l, (l + r) / 2); int rch = getMax(a, b, k * 2 + 2, (l + r) / 2, r); return (max(lch, rch) + datb[k]); } void add(int a, int b, int x, int k, int l, int r) { if (b <= l || r <= a) return; if (a <= l && r <= b){ datb[k] += x; while (k){ k = (k - 1) / 2; data[k] = max(data[k * 2 + 1] + datb[k * 2 + 1], data[k * 2 + 2] + datb[k * 2 + 2]); } } else { add(a, b, x, k * 2 + 1, l, (l + r) / 2); add(a, b, x, k * 2 + 2, (l + r) / 2, r); } } int main(void) { int N, H; int di[100000], hi[100000]; int curLife; scanf("%d %d", &N, &H); for (int i = 0; i < N - 1; i++){ scanf("%d %d", &di[i], &hi[i]); } curLife = H; ll ans = 0; priority_queue<Dungeon> que; for (int i = 0; i < N - 1; i++){ add(i, i + 1, curLife, 0, 0, 1 << 17); Dungeon in; curLife -= di[i]; in.plus = hi[i]; in.pos = i; que.push(in); while (curLife <= 0){ Dungeon heal = que.top(); que.pop(); int segMax = getMax(heal.pos, i + 1, 0, 0, 1 << 17); if (heal.plus + segMax > H){ heal.plus = H - segMax; que.push(heal); continue; } int howMany = min((-1 * curLife) / heal.plus + 1, (H - segMax) / heal.plus); ans += howMany; curLife += howMany * heal.plus; add(heal.pos, i + 1, howMany * heal.plus, 0, 0, 1 << 17); que.push(heal); } } printf("%lld\n", ans); return (0); }