#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <utility>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <functional>
using namespace std;
typedef long long ll;
typedef vector<ll> vl;
typedef vector<vector<ll>> vvl;
typedef pair<ll, ll> P;
typedef vector<vector<P>> vvP;
#define rep(i, n) for(ll i = 0; i < n; i++)
#define exrep(i, a, b) for(ll i = a; i <= b; i++)
#define out(x) cout << x << endl
#define exout(x) printf("%.10f\n", x)
#define chmax(x, y) x = max(x, y)
#define chmin(x, y) x = min(x, y)
#define all(a) a.begin(), a.end()
#define rall(a) a.rbegin(), a.rend()
#define pb push_back
#define re0 return 0
const ll mod = 1000000007;
const ll INF = 1e16;
const ll MAX_N = 100010;

// 以下の遅延伝播セグメント木は長さnの数列に対して
// 1. 区間[a,b)にxを加算する
// 2. 区間[a,b)の総和を求める
// という操作をO(log(n))で処理できる

struct LazySegmentTree {
private:
    ll n;
    vector<ll> node, lazy;

public:

    LazySegmentTree(vector<ll> v) {
        ll sz = v.size();
        n = 1;
        while(n < sz) {
            n *= 2;
        }
        node.resize(2*n - 1);
        lazy.resize(2*n - 1, 0);
        rep(i, sz) {
            node[i+n-1] = v[i];
        }
        for(ll i = n-2; i >= 0; i--) {
            node[i] = node[2*i + 1] + node[2*i + 2];
        }
    }

    // k番目のノードについて遅延評価を行う
    void eval(ll k, ll l, ll r) {
        // 遅延配列が空でない場合、自ノード及び子ノードへの値の伝播が起こる
        if(lazy[k] != 0) {
            node[k] += lazy[k];
            // 子ノードは親ノードの1/2の範囲であるため伝播させるときは半分にする
            if(r - l > 1) {
                lazy[2*k + 1] += lazy[k]/2;
                lazy[2*k + 2] += lazy[k]/2;
            }
            // 伝播が終わったので、自ノードの遅延配列を空にする
            lazy[k] = 0;
        }
    }

    // 区間[a,b)にxを加算する
    void add(ll a, ll b, ll x, ll k = 0, ll l = 0, ll r = -1) {
        if(r < 0) {
            r = n;
        }
        // k番目のノードに対して遅延評価を行う
        eval(k, l, r);
        // 範囲外なら何もしない
        if(b <= l || r <= a) {
            return;
        }
        // 完全に被覆しているならば遅延配列に値を入れた後に評価
        if(a <= l && r <= b) {
            lazy[k] += (r - l)*x;
            eval(k, l, r);
        }
        // そうでないならば子ノードの値を再帰的に計算する
        else {
            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[2*k + 1] + node[2*k + 2];
        }
    }
    
    // 区間[a,b)の総和を返す
    ll getsum(ll a, ll b, ll k = 0, ll l = 0, ll r = -1) {
        // 区間[a,b)に対するクエリについてノードk(区間[l, r)担当)が答える
        if(r < 0) {
            r = n;  // 最初に呼び出されたときの対象区間は[0,n)
        }
        if(b <= l || r <= a) {
            return 0;  // 区間が被らない場合は0を返す
        }
        // 関数が呼び出されたら評価
        eval(k, l, r);
        if(a <= l && r <= b) {
            return node[k];  // ノードkの担当範囲が区間[a,b)に完全に含まれる
        }
        // 一部だけ範囲が被る場合は左右の子に委託する
        ll c1 = getsum(a, b, 2*k + 1, l, (l + r)/2);
        ll c2 = getsum(a, b, 2*k + 2, (l + r)/2, r);
        return c1 + c2;  // 左右の子の値の和をとる
    }
};

int main() {
    ll n, d, a;
    cin >> n >> d >> a;

    vector<P> p(n);
    rep(i, n) {
        ll x, h;
        cin >> x >> h;
        p[i] = make_pair(x, h);
    }
    sort(all(p));

    vl x(n), h(n);
    rep(i, n) {
        x[i] = p[i].first;
        h[i] = p[i].second;
    }
    
    LazySegmentTree lazyseg(vector<ll>(n, 0));
    rep(i, n) {
        lazyseg.add(i, i+1, h[i]);
    }

    ll ans = 0;
    rep(i, n) {
        ll hp_i = lazyseg.getsum(i, i+1);  // モンスタiの体力
        if(hp_i <= 0) {  // モンスタiの体力が0以下の場合
            continue;  // モンスタiはおしまい
        }
        ll cnt = (hp_i + a - 1)/a;  // モンスタiを倒すのに必要な操作回数
        //　モンスタiのいる座標を左端とした爆発が届く範囲を求める
        ll right = x[i] + 2*d;  // 爆発が届く右端の座標
        ll id = upper_bound(all(x), right) - x.begin();  // 爆発が届かないモンスタの中で最も左にいるモンスタの番号 
        lazyseg.add(i, id, -a*cnt);  // モンスタiからモンスタid-1の体力をa*need減らす
        ans += cnt;
    }
    
    out(ans);
    re0;
}

Measured memory_bytes: 16256