Problem:
ダンジョン

問題

あなたはあるダンジョンの地下 N 階にある財宝を手に入れたいと思っている.最初,あなたは地下 1 階におり,あなたの体力は H(H は正整数) である.下の階に降りるときに,体力が消費される.あらかじめ各階における下の階に降りるときに消費される体力が分かっている.一方,各階には 1 つの回復の泉があり,泉を 1 回使うごとに回復できる体力がそれぞれ定まっている.体力が 0 以下になるとあなたは死んでしまう.また,体力が H よりも高くなることはない.回復の泉は何回でも使うことができるが,回復には時間がかかるので,あなたは泉の使用回数をできるだけ少なくしたいと考えている.

N ,H ,各階の下の階に降りるときに消費される体力,そして各階で回復の泉を 1 回使用したときに回復する体力が与えられたとき,体力を 0 以下にすることなく地下 N 階まで到達するために必要な泉の使用回数の最小値を求めるプログラムを作成せよ.

また,一度下の階に降りると,財宝を手に入れるまで上の階に戻ることはできない.

入力

入力は N 行からなる.
入力の 1 行目には 2 つの整数 N, H(2 ≤ N ≤ 100000 = 105, 1 ≤
H ≤ 10000000 = 107 ) が空白を区切りとして書かれている.N は地下 N 階に財宝があるということを表し,H は初期体力 ( ダンジョンの地下 1 階に到着した段階の体力 ) であり,かつ体力の最大値である ( 回復によって体力が H より大きくなることはない ) .

続く N − 1 行には,2 つの整数が空白を区切りとして書かれている.1 + i 行目(1 ≤ i ≤ N − 1) の整数 di , hi について, di は地下 i 階から地下 i + 1 階に降りる際に消費される体力を,hi は地下 i 階で泉を 1 回使用したときに回復する体力を表す(0 ≤ di < H, 1 ≤ hi < H).

どの採点用データにおいても,地下 N 階にたどり着く方法が存在する.採点用データのうち,配点の 10%分については,N ≤
1000 かつ H ≤
1000 を満たす.配点の30%分については,N ≤1000 を満たす.配点の 50%分については,泉の使用回数の最小値が 106 を超えない.

出力

出力は,体力を 0 以下にすることなく地下 N 階に到達するために必要な,泉の使用回数の最小値を含む 1 行からなる.

注意

この問題では,扱う整数の範囲が 32bit に収まらない可能性があることに注意せよ.

入出力例

入力例 1

10 10
4 2
2 5
6 1
7 3
6 4
9 6
0 8
4 1
9 4

出力例 1

10

上記問題文と自動審判に使われるデータは、情報オリンピック日本委員会が作成し公開している問題文と採点用テストデータです。

Solution in C++ (judge verdict: Accepted)

#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];

void add(int a, int b, int x, int k, int l, int r)
{
    if (a <= l && r <= b){ //comletely inclusive.
        data[k] += x; //add to all segmets corresponds to [l, r).
    }
    else if (l < b && a < r){ //some of the segments are inclusive.
        datb[k] += (min(b, r) - max(a, l)) * x;  //add the x to the segments that belongs to [max(a, l), min(r, b)).
        add(a, b, x, k * 2 + 1, l, (l + r) / 2); //go to the left vertex.
        add(a, b, x, k * 2 + 2, (l + r) / 2, r); //go to the right vertex.
    }
}

//*
int sum(int a, int b, int k, int l, int r)
{
    if (b <= l || r <= a){ //looking segment[l, r) cross with the segment [a, b).
        return (0);
    }
    else if (a <= l && r <= b){ //completely inclusive.
        return (data[k] * (r - l) + datb[k]);
    }
    else { //some of the segments are inclusive.
        ll res;
        res = (min(b, r) - max(a, l)) * data[k];
        res += sum(a, b, k * 2 + 1, l, (l + r) / 2);
        res += sum(a, b, k * 2 + 2, (l + r) / 2, r);
        return (res);
    }
}
//*/

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();
            
            /* O(logN)
            int segMax = getMax(heal.pos, i + 1, 0, 0, 1 << 17);
            //*/
            
            //* O(NlogN)
            int segMax = 0;
            for (int j = heal.pos; j <= i; j++){
                segMax = max(segMax, sum(j, j + 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);
}