//g++ -std=c++14 test.cpp -o test.out
//問題URL
//https://atcoder.jp/contests/abc153/tasks/abc153_f
//ある区間に対して同じ操作→segment tree or BIT
//を使えばO(logN)でできる

//segment treeに関する記事:
//https://qiita.com/ageprocpp/items/f22040a57ad25d04d199

//おおざっぱには、数列に対して次のような処理を、
//数列の長さを N として、一回 O(logN) で行えるのがSegment Treeです。
//数列のどこかの値になんらかの演算をする。（変更する、値を加算するなど）
//数列の一部の区間になんらかの演算をした結果を求める。（最小値を求める、総和を求めるなど）

//Segment Treeにのせる演算は、次の要件を満たす必要があります。
//数列の各要素と、区間に対して行う演算が結合法則を満たす。（交換法則は満たさなくてよい）
//要は a+(b+c) = (a+b)+c を満たすような、どの順に計算しても結果が変わらない数列なら良い

//遅延Segment tree
//http://tsutaj.hatenablog.com/entry/2017/03/30/224339

//コード参考
//https://yamakasa.net/atcoder-abc-153-f/

#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <cassert>
#include <algorithm>
#include <functional>
#include <iostream>
#include <iomanip>
#include <vector>
#include <queue>
#include <map> 
#include <utility>
#include <limits.h>
#include <bitset>
#include <set>
using namespace std;
 
#define LL long long int
const LL INF = (LL)(1e18)+1;
const int INF_INT = 2147483647-1e6;
const LL mod = 1000000007ll;
const int mod_int = 1000000007;

class SegmentTree {
private:
    int n;//元のデータの長さ
    vector<LL> node;//各ノードの値を保管
    vector<LL> lazy;//遅延配列
public:
    //初期化処理
    SegmentTree(int N){
        n = 1;
        //segment treeのノードの数＝n+n/2+n/4+....=2n
        //＋あらかじめnはnよりも大きい最小の2の冪乗にしておく
        while (n<N) n *= 2;
        node.resize(2*n - 1, 0);//各ノードの値を保管する配列
        lazy.resize(2*n - 1, 0);//遅延配列
    }
    //ノードiの値に対し遅延評価、iが子を持つならそこに伝搬もする
    //ノードiは元の数列の[l,r)の和を格納している
    void update(int i,int l,int r){
        if (lazy[i] == 0) return;//遅延配列が0なら何もしない
        node[i] += lazy[i];//ノードiの値に遅延配列の値を加算
        //最下段かどうかのチェック
        //r-l=1なら[l,r)は元の数列[l]のみを要素にもつ
        //これはつまりセグ木の末端にいることを意味するので
        //このときノードiは子を持たない→伝搬する必要なし
        if (r - l > 1) {
            //子ノードは親ノードの 1/2 の範囲であるため
            //伝播させるときは半分にする
            //親から子にいく：ノード番号に2をかけて+1,+2
            //子から親にいく：ノード番号から1引いて2で割り切り捨て
            lazy[2*i + 1] += lazy[i] / 2;
            lazy[2*i + 2] += lazy[i] / 2;
        }
        //伝播が終わったので、自ノードの遅延配列を空にする
        lazy[i] = 0;
    }
    //加算クエリ 
    //加算クエリでは遅延配列を更新して足された状態を表現する
    //元の数列の長さをNとしたとき加算クエリは計算量O(logN)
    //区間に対しての更新なので、最上段から下がっていくイメージ
    //元の数列の[a,b)に対しxを加算する関数
    //kは節点の番号、l,rはその節点が
    //元の数列の[l,r)の和を格納していることを表す
    void add(int a,int b,LL x,int k=0,int l=0,int r=-1){
        if (r < 0) r = n;
        update(k,l,r);//k番目のノードに対し遅延評価
        if (b <= l || r <= a) return;//範囲外なら何もしない
        //要求区間が対象区間を完全に被覆しているなら
        //遅延配列に値を入れた後に評価
        if (a <= l && r <= b) {
            lazy[k] += (r - l) * x;//区間の長さ*増える数
            update(k,l,r);//これを呼ぶとノードkの値が更新される
        }
        //要求区間が対象区間を完全に被覆していないなら
        else {
            //node[2*k+1]とnode[2*k+2]の値を求めて
            //(節点2k+1は元の数列の[l,(l+r)/2)の和を格納していて
            //(節点2k+2は元の数列の[(l+r)/2,r)の和を格納している
            add(a,b,x,2*k + 1,l,(l+r)/2);
            add(a,b,x,2*k + 2,(l+r)/2,r);
            //その和をnode[k]とする
            node[k] = node[2*k + 1] + node[2*k + 2];
        }
    }
    //元の数列の[a,b)の和を返す関数
    //O(logN)
    //kは節点の番号、l,rはその節点が
    //元の数列の[l,r)の和を格納していることを表す
    //外からはquery(a,b,0,0,n)として呼び出す
    //query(a,b)でも可
    LL query(int a,int b,int k=0,int l=0,int r=-1){
        if (r < 0) r = n;
        update(k, l, r);
        //要求区間とその節点が属す区間が全く交差していなければ0を返しておく
        if (b <= l || r <= a) return 0;  
        //要求区間が完全に[l,r)を被覆しているならノードkの値を返す     
        if (a <= l && r <= b) return node[k];
 
        LL sum_l = query(a,b,2*k + 1,l,(l+r)/2);
        LL sum_r = query(a,b,2*k + 2,(l+r)/2,r);
        return sum_l + sum_r;
    }
};

LL N,D,A;
vector<pair<LL,LL>> monster;

LL ans;

void solve(){
    sort(monster.begin(),monster.end());//モンスターの位置順にソート
    SegmentTree st(N);//長さNのベクトル(0,0,0,....,0)で初期化
    //セグ木には各モンスターに与えたダメージを格納していく
    int size = 1;//Nを超える最小の2の冪乗の数
    while (N < size) size *= 2;
    for (int i = 0; i < N; i++) {
        //二部探索
        //例えばモンスターiが範囲の一番左側の時
        //X_j <= X_i + 2D
        //を満たす最大のjまでが爆風に入る
        int left = i - 1;
        int right = N;
        while (right - left > 1) {
            int mid = (left + right) / 2;
            if (monster[mid].first <= monster[i].first + 2 * D) {
                left = mid;
            } else {
                right = mid;
            }
        }
        //この時点でleftが条件を満たす最大のjのモンスターを指す
        
        LL damage = st.query(i,i+1);//i番目のモンスターに与えたダメージの合計を取得
        //segment treeからの値の取得はO(logN)
        //Aは威力
        if (monster[i].second - damage > 0) {
            LL remain_hp = monster[i].second - damage;
            LL numof_attack = (remain_hp + A - 1) / A;//攻撃回数
            ans += numof_attack;
            //segment treeへの値の操作はO(logN)
            st.add(i,left+1,numof_attack*A);
            //インデックス[i,left+1)のモンスターにnumof_attack*Aダメージを与える
        }
    }
}
 
int main(){
    cin >> N >> D >> A;
    for(int i=0;i<N;i++){
        LL x,h;
        cin >> x >> h;
        monster.push_back(pair<LL,LL>(x,h));
    }
 
    solve();

    cout << ans << endl;
    return 0;
}

Measured memory_bytes: 12652