#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll

template<typename T1,typename T2> inline void chmin(T1 &a, T2 b){ if(a > b) a = b; }
template<typename T1,typename T2> inline void chmax(T1 &a, T2 b){ if(a < b) a = b; }

#define REP(i,n) for(int i=0;i<n;++i)
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 0
#  define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#  define DBG(fmt, ...)
#endif

const ll LLINF = (1LL<<60);
const int INF = (1LL<<30);
const double DINF = std::numeric_limits<double>::infinity();
const int MOD = 1000000007;
const int MAX_N = 100010;

// 区間加算・区間更新・区間和を求める遅延セグ木
// 区間への加算、更新、和の導出が O(log N) でできる
// メモリは 3N * sizeof(ll) 消費
// 加算と更新は実装をどちらか一つにするとより高速化できる
class LazySegmentTree {
    private:
        // 遅延評価の種類を表す enum
        enum Status {
            None = 0,           // 遅延評価なし
            Update_Add = 1,     // 加算の遅延評価
            Update_Reset = 2    // 更新の遅延評価
        };

    public:
        LazySegmentTree() {}
        LazySegmentTree(ll n) { Init(n); }
        // 初期化
        // n: セグメント木で扱う配列の長さ
        void Init(ll n) {
            // 要素数を 2 べきにする
            m_Size = 1;
            while(m_Size < n) { m_Size <<= 1; }
            m_Seg.assign(2 * m_Size, 0);    // 単位元で初期化
            m_Lazy.assign(2 * m_Size, 0);
            m_LazyFlag.assign(2 * m_Size, None);
        }
        // k 番目の要素に x をセット (0-indexed)
        void Set(ll k, ll x) {
            assert(0 <= k && k < m_Size);
            m_Seg[k + m_Size] = x;
        }
        // セグ木構築 O(N)
        void Build() {
            for(int k = m_Size - 1; k > 0; k--) {
                // 子ノードに対しての演算結果を親ノードに格納
                m_Seg[k] = m_Seg[2 * k + 0] + m_Seg[2 * k + 1];
            }
        }
        // 区間 [a, b) に対して x を足します。 O(log N)
        void Add(ll a, ll b, ll x) {
            assert(0 <= a && a <= b && b <= m_Size);
            AddImpl(a, b, x, 1, 0, m_Size);
        }
        // 区間 [a, b) 全ての要素を x にします。 O(log N)
        void Reset(ll a, ll b, ll x) {
            assert(0 <= a && a <= b && b <= m_Size);
            ResetImpl(a, b, x, 1, 0, m_Size);
        }
        // 区間 [a, b) の総和を返します。 O(log N)
        ll Query(ll a, ll b) {
            assert(0 <= a && a <= b && b <= m_Size);
            return QueryImpl(a, b, 1, 0, m_Size);
        }
        // セグ木で管理している要素の k 番目にアクセスします O(log N)
        ll operator[](const ll &k) {
            assert(0 <= k && k < m_Size);
            return Query(k, k + 1);
        }

    private:
        // セグ木の k 地点からの区間遅延評価を行います
        void Propagate(ll k) {
            if(m_LazyFlag[k] == Update_Add) {
                if(k < m_Size) {
                    // 違うタイプのものがまだ伝搬していないなら更に下に伝搬させておく
                    if(m_LazyFlag[2 * k + 0] == Update_Reset) { Propagate(2 * k + 0); }
                    if(m_LazyFlag[2 * k + 1] == Update_Reset) { Propagate(2 * k + 1); }
                    // 子があるなら子の遅延配列に値を伝搬
                    // 子の範囲は親の半分なので /2 する
                    m_Lazy[2 * k + 0] += m_Lazy[k] / 2;
                    m_Lazy[2 * k + 1] += m_Lazy[k] / 2;
                    m_LazyFlag[2 * k + 0] = m_LazyFlag[2 * k + 1] = Update_Add;
                }
                m_Seg[k] += m_Lazy[k];  // 遅延評価ぶんの反映
                m_Lazy[k] = 0;          // 反映後クリア
                m_LazyFlag[k] = None;
            }
            else if(m_LazyFlag[k] == Update_Reset) {
                if(k < m_Size) {
                    m_Lazy[2 * k + 0] = m_Lazy[2 * k + 1] = m_Lazy[k] / 2;
                    m_LazyFlag[2 * k + 0] = m_LazyFlag[2 * k + 1] = Update_Reset;
                }
                m_Seg[k] = m_Lazy[k];
                m_Lazy[k] = 0;
                m_LazyFlag[k] = None;
            }
        }
        // 区間 [a, b) に対して x を足します O(log N)
        // そのうち m_Seg のインデックス k で管理される区間 [l, r) に対して処理します
        // 0-indexed
        void AddImpl(ll a, ll b, ll x, ll k, ll l, ll r) {
            Propagate(k);   // 管理範囲を遅延評価
            if(b <= l || r <= a) { return; }   // 範囲外
            if(a <= l && r <= b) {
                // 完全に包含しているなら
                // 管理している範囲の要素数 * x だけ加算すればよい
                m_Lazy[k] += (r - l) * x;
                m_LazyFlag[k] = Update_Add;
                // その後遅延評価
                Propagate(k);
            }
            else {
                // 範囲が中途半端なら再帰
                AddImpl(a, b, x, 2 * k + 0, l, (l + r) >> 1);
                AddImpl(a, b, x, 2 * k + 1, (l + r) >> 1, r);
                m_Seg[k] = m_Seg[2 * k + 0] + m_Seg[2 * k + 1];
            }
        };
        // 区間 [a, b) 全ての要素を x にします O(log N)
        // そのうち m_Seg のインデックス k で管理される区間 [l, r) に対して処理します
        // 0-indexed
        void ResetImpl(ll a, ll b, ll x, ll k, ll l, ll r) {
            Propagate(k);
            if(b <= l || r <= a) { return; }
            if(a <= l && r <= b) {
                m_Lazy[k] = (r - l) * x;
                m_LazyFlag[k] = Update_Reset;
                // その後遅延評価
                Propagate(k);
            }
            else {
                // 範囲が中途半端なら再帰
                ResetImpl(a, b, x, 2 * k + 0, l, (l + r) >> 1);
                ResetImpl(a, b, x, 2 * k + 1, (l + r) >> 1, r);
                m_Seg[k] = m_Seg[2 * k + 0] + m_Seg[2 * k + 1];
            }
        };
        // 区間 [a,b) の総和を返すために、
        // そのうち m_Seg のインデックス k で管理される区間 [l, r) に対して処理します
        // O(log N)
        ll QueryImpl(ll a, ll b, ll k, ll l, ll r) {
            Propagate(k);
            if(b <= l || r <= a) { return 0; }
            if(a <= l && r <= b) { return m_Seg[k]; }
            return QueryImpl(a, b, 2 * k + 0, l, (l + r) >> 1) +
                   QueryImpl(a, b, 2 * k + 1, (l + r) >> 1, r);
        }

    private:
        ll m_Size;                  // セグ木の要素数
        vector<ll> m_Seg;           // セグ木本体。一次元で管理
        vector<ll> m_Lazy;          // 遅延評価伝搬配列
        vector<ll> m_LazyFlag;      // どのタイプの遅延評価を行うかのフラグ
};

// [lb, ub) の範囲で val 以下の要素で最大のもののインデックスを返す
// なければ -1 を返す
// 同じ数が複数ある場合は一番小さいインデックスを返す
static inline ll bin_search4(const vector< pair<ll, ll> >& v, ll lb, ll ub, ll val) {
    lb--; ub--;
    while(ub - lb > 1) {
        ll mid = (lb + ub) / 2;
        if(v[mid].first >= val) { ub = mid; }
        else { lb = mid; }
    }
    // この時点で lb + 1 = ub
    if(v[ub].first <= val) { return ub; }
    if(lb < 0 || v[lb].first > val) { return -1; }
    return lb;
}

ll N, D, A;
vector< pair<ll, ll> > XH;

signed main() {
    cin.tie(0); cout.tie(0);
    ios::sync_with_stdio(false);

    cin >> N >> D >> A;
    REP(i, N) {
        ll x, h;
        cin >> x >> h;
        XH.push_back(make_pair(x, h));
    }

    SORT(XH);
    LazySegmentTree lst(N);     // 各モンスターに与えたダメージ
    REP(i, N) {
        lst.Set(i, 0);
    }
    lst.Build();

    ll ans = 0;
    REP(i, N) {
        ll cur_h = XH[i].second - lst.Query(i, i + 1);
        DBG("i: %lld cur_h: %lld XH[i].second: %lld lst.Query(i, i + 1): %lld\n", i, cur_h, XH[i].second, lst.Query(i, i + 1));
        if(cur_h <= 0) { continue; }
        ll cnt = cur_h / A;
        DBG("  cnt: %lld\n", cnt);
        if(cur_h % A != 0) { cnt++; }
        ans += cnt;
        ll damage = A * cnt;
        // ダメージが届く範囲
        ll x_r = XH[i].first + (2 * D);
        assert(x_r >= i);
        ll range = bin_search4(XH, i, N, x_r);
        lst.Add(i, range + 1, damage);
    }
    printf("%lld\n", ans);

    return 0;
}


Measured memory_bytes: 16620