Buggy python submission (verdict: Time Limit Exceeded):
import operator
import os
import sys

if os.getenv("LOCAL"):
    sys.stdin = open("_in.txt", "r")

sys.setrecursionlimit(10 ** 9)
INF = float("inf")
IINF = 10 ** 18
MOD = 10 ** 9 + 7


# MOD = 998244353


class LazySegmentTree:
    # http://tsutaj.hatenablog.com/entry/2017/03/29/204841
    def __init__(self, size, fn=operator.add, default=None, initial_values=None):
        """
        :param int size:
        :param callable fn: 区間に適用する関数。引数を 2 つ取る。min, max, operator.xor など
        :param default:
        :param list initial_values:
        """
        default = default or 0

        # size 以上である最小の 2 冪を size とする
        self._size = 1 << (size - 1).bit_length()
        self._fn = fn

        self._lazy = [0] * (self._size * 2 - 1)
        self._tree = [default] * (self._size * 2 - 1)
        if initial_values:
            i = self._size - 1
            for v in initial_values:
                self._tree[i] = v
                i += 1
            i = self._size - 2
            while i >= 0:
                self._tree[i] = self._fn(self._tree[i * 2 + 1], self._tree[i * 2 + 2])
                i -= 1

    def add(self, from_i, to_i, value, k=0, L=None, r=None):
        """
        [from_i, to_i) を、それぞれの値と value に fn を適用した値で更新する
        :param int from_i:
        :param int to_i:
        :param int value:
        :param int k: self._tree のインデックス
        :param int L:
        :param int r:
        :return:
        """
        L = 0 if L is None else L
        r = self._size if r is None else r

        self._eval(k, L, r)

        # 範囲外
        if to_i <= L or r <= from_i:
            return

        if from_i <= L and r <= to_i:
            # 完全に被覆してる
            self._lazy[k] += (r - L) * value
            self._eval(k, L, r)
        else:
            # 中途半端
            self.add(from_i, to_i, value, k * 2 + 1, L, (L + r) // 2)
            self.add(from_i, to_i, value, k * 2 + 2, (L + r) // 2, r)
            self._tree[k] = self._fn(self._tree[k * 2 + 1], self._tree[k * 2 + 2])

    def _eval(self, k, L, r):
        """
        遅延配列の値を評価する
        :param k:
        :param L:
        :param r:
        """
        if self._lazy[k] != 0:
            # 本体を更新
            self._tree[k] += self._lazy[k]
            # 一番下じゃなければ伝播させる
            if r - L > 1:
                self._lazy[k * 2 + 1] += self._lazy[k] >> 1
                self._lazy[k * 2 + 2] += self._lazy[k] >> 1
            self._lazy[k] = 0

    def get(self, from_i, to_i, k=0, L=None, r=None):
        """
        [from_i, to_i) に fn を適用した結果を返す
        :param int from_i:
        :param int to_i:
        :param int k: self._tree[k] が、[L, r) に fn を適用した結果を持つ
        :param int L:
        :param int r:
        :return:
        """
        L = 0 if L is None else L
        r = self._size if r is None else r

        self._eval(k, L, r)

        if from_i <= L and r <= to_i:
            return self._tree[k]

        if to_i <= L or r <= from_i:
            return None

        ret_L = self.get(from_i, to_i, k * 2 + 1, L, (L + r) // 2)
        ret_r = self.get(from_i, to_i, k * 2 + 2, (L + r) // 2, r)
        if ret_L is None:
            return ret_r
        if ret_r is None:
            return ret_L
        return self._fn(ret_L, ret_r)

    def __len__(self):
        return self._size


N, D, A = list(map(int, sys.stdin.buffer.readline().split()))
XH = [list(map(int, sys.stdin.buffer.readline().split())) for _ in range(N)]

XH.sort()
X = []
H = []
for x, h in XH:
    X.append(x)
    H.append(h)

lr = []
r = 0
for l in range(N):
    while r < N and X[r] <= X[l] + D * 2:
        r += 1
    lr.append((l, r))

st = LazySegmentTree(size=N, initial_values=H)
ans = 0
for l, r in lr:
    h = st.get(l, l + 1)
    if h > 0:
        cnt = (h + A - 1) // A
        st.add(l, r, -cnt * A)
        ans += cnt
print(ans)


Fixed submission:
import operator
import os
import sys

if os.getenv("LOCAL"):
    sys.stdin = open("_in.txt", "r")

sys.setrecursionlimit(10 ** 9)
INF = float("inf")
IINF = 10 ** 18
MOD = 10 ** 9 + 7


# MOD = 998244353


class LazySegmentTree:
    # http://tsutaj.hatenablog.com/entry/2017/03/29/204841
    def __init__(self, size, fn=operator.add, default=None, initial_values=None):
        """
        :param int size:
        :param callable fn: 区間に適用する関数。引数を 2 つ取る。min, max, operator.xor など
        :param default:
        :param list initial_values:
        """
        default = default or 0

        # size 以上である最小の 2 冪を size とする
        self._size = 1 << (size - 1).bit_length()
        self._fn = fn

        self._lazy = [0] * (self._size * 2 - 1)
        self._tree = [default] * (self._size * 2 - 1)
        if initial_values:
            i = self._size - 1
            for v in initial_values:
                self._tree[i] = v
                i += 1
            i = self._size - 2
            while i >= 0:
                self._tree[i] = self._fn(self._tree[i * 2 + 1], self._tree[i * 2 + 2])
                i -= 1

    def add(self, from_i, to_i, value, k=0, L=None, r=None):
        """
        [from_i, to_i) を、それぞれの値と value に fn を適用した値で更新する
        :param int from_i:
        :param int to_i:
        :param int value:
        :param int k: self._tree のインデックス
        :param int L:
        :param int r:
        :return:
        """
        L = 0 if L is None else L
        r = self._size if r is None else r

        self._eval(k, L, r)

        # 範囲外
        if to_i <= L or r <= from_i:
            return

        if from_i <= L and r <= to_i:
            # 完全に被覆してる
            self._lazy[k] += (r - L) * value
            self._eval(k, L, r)
        else:
            # 中途半端
            self.add(from_i, to_i, value, k * 2 + 1, L, (L + r) // 2)
            self.add(from_i, to_i, value, k * 2 + 2, (L + r) // 2, r)
            self._tree[k] = self._fn(self._tree[k * 2 + 1], self._tree[k * 2 + 2])

    def _eval(self, k, L, r):
        """
        遅延配列の値を評価する
        :param k:
        :param L:
        :param r:
        """
        if self._lazy[k] != 0:
            # 本体を更新
            self._tree[k] += self._lazy[k]
            # 一番下じゃなければ伝播させる
            if r - L > 1:
                self._lazy[k * 2 + 1] += self._lazy[k] >> 1
                self._lazy[k * 2 + 2] += self._lazy[k] >> 1
            self._lazy[k] = 0

    def get(self, from_i, to_i, k=0, L=None, r=None):
        """
        [from_i, to_i) に fn を適用した結果を返す
        :param int from_i:
        :param int to_i:
        :param int k: self._tree[k] が、[L, r) に fn を適用した結果を持つ
        :param int L:
        :param int r:
        :return:
        """
        L = 0 if L is None else L
        r = self._size if r is None else r

        self._eval(k, L, r)

        if from_i <= L and r <= to_i:
            return self._tree[k]

        if to_i <= L or r <= from_i:
            return None

        ret_L = self.get(from_i, to_i, k * 2 + 1, L, (L + r) // 2)
        ret_r = self.get(from_i, to_i, k * 2 + 2, (L + r) // 2, r)
        if ret_L is None:
            return ret_r
        if ret_r is None:
            return ret_L
        return self._fn(ret_L, ret_r)

    def __len__(self):
        return self._size


N, D, A = list(map(int, sys.stdin.buffer.readline().split()))
XH = [list(map(int, sys.stdin.buffer.readline().split())) for _ in range(N)]

XH.sort()
X = []
H = []
for x, h in XH:
    X.append(x)
    H.append(h)

lr = []
r = 0
for l in range(N):
    while r < N and X[r] <= X[l] + D * 2:
        r += 1
    lr.append((l, r))

imos = [0] * (N + 1)
cum = 0
ans = 0
for l, r in lr:
    cum += imos[l]
    h = H[l] - cum
    if h > 0:
        cnt = (h + A - 1) // A
        cum += cnt * A
        imos[r] -= cnt * A
        ans += cnt
print(ans)