import std.stdio, std.conv, std.string, std.array, std.range, std.algorithm, std.container;
import std.math, std.random, std.bigint, std.datetime, std.format;
void main(string[] args){ if(args.length > 1) if(args[1] == "-debug") DEBUG = 1; solve(); }
void log()(){ writeln(""); } void log(T, A ...)(T t, lazy A a){ if(DEBUG) write(t, " "), log(a); } bool DEBUG = 0; 
string rstring(){ static string[] ss; while(!ss.length) ss = readln.chomp.split; string res = ss[0]; ss.popFront; return res; }
T rtype(T)(){ return rstring.to!T; } alias rint = rtype!int, rlong = rtype!long, rreal = rtype!real;
T[] rtypes(T)(int n){ return n.iota.map!(i => rtype!T()).array; } alias rint = rtypes!int, rlong = rtypes!long, rreal = rtypes!real;
T[][] rtypess(T)(int n, int m){ return n.iota.map!(i => rtypes!T(m)).array; } alias rint = rtypess!int, rlong = rtypess!long, rreal = rtypess!real;
T chmin(T)(ref T x, T y){ if(x > y) x = y; return x; } T chmax(T)(ref T x, T y){ if(x < y) x = y; return x; }

// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- //

void solve(){
	int n = rint;
	long d = rlong, a = rlong;
	M[] ms;
	foreach(i; 0 .. n) ms ~= M(rlong, rlong, -2);

	ms.sort!"a.x<b.x"();
	log("ms:", ms);

	// 二分探索テンプレート
	T uplimit(T)(T a, T c, bool delegate(T) f){
		if(f(c)) return c; if(! f(a)) return a - 1;
		while(a + 1 < c){ T b = (a + c) / 2; if(f(b)) a = b; else c = b; }
		return a;
	}
	foreach(i, m; ms){
		int u = uplimit(0, n - 1, (int j) => ms[j].x <= m.x + d + d);
		ms[i] = M(m.x, m.h, u);
	}
	log("ms:", ms);

	auto seg = new SegTree!long(0, n, (long x, long y) => x + y, 0);

	foreach(int i, m; ms){
		seg.setValue(i, i + 1, (m.h + a - 1) / a);
	}
	log("seg:", seg);

	long ans = 0;
	foreach(int i, m; ms){
		long v = seg.getValue(i);
		if(v <= 0) continue;
		else{
			ans += v;
			seg.setValue(i, m.u + 1, -v);
		}
		log("i:", i, "m:", m, "seg:", seg, "ans:", ans);
	}

	ans.writeln;

}

struct M{
	long x, h;
	int u;
}

/*
セグメント木 区分木 segtree segment tree
範囲に書き込み、1点で読み出す。
たとえば、 [l, r) の範囲に一律 x を足すクエリがくるなど。

※書き込み操作は、単位元みたいなものが存在しなければならない
（「すべての x に対して f(z, x) = f(x, z) = x」なる z ）
たとえば、+ や `max` など

引数
a, b　添字の範囲。 この意味はa から b - 1 までなので注意。
apply　(x, y) => すでに値 x があるところに y を加えるとどうなるか ※結合的な演算である必要がある
neutral　すべての x に対して apply(z, x) = apply(x, z) = x をみたす z

*/
class SegTree(T){
	T delegate(T, T) apply;
	T neutral;
	int a, b; // a <= x < b を担当する
	bool isTerminal;
	SegTree left, right; // 左の子(a <= x < c)、右の子(c <= x < b)
	T value;
	this(int a, int b, T delegate(T, T) apply, T neutral = T.init){
		this.a = a, this.b = b;
		this.apply = apply;
		this.neutral = neutral;
		this.value = neutral;
		if(b - a == 1){
			this.isTerminal = 1;
		}
		else{
			int c = (a + b) / 2;
			this.left = new SegTree(a, c, apply, neutral);
			this.right = new SegTree(c, b, apply, neutral);
		}
	}
	
	// [a, b) に値を設定する ※[a, b) はこのノードの担当区間とかぶっていなくてもよい
	void setValue(int a, int b, T value){
		if(b <= this.a || this.b <= a) return;
		if(a <= this.a && this.b <= b){
			this.value = apply(this.value, value);
		}
		else{
			divideValue();
			left.setValue(a, b, value);
			right.setValue(a, b, value);
		}
	}
	
	// 自分の持っていた値を子に引き継ぐ
	void divideValue(){
		left.value = apply(left.value, this.value);
		right.value = apply(right.value, this.value);
		this.value = neutral;
	}
	
	// iにおける値 ※iは必ずこのノードの担当区間内である前提
	T getValue(int i){
		assert(a <= i && i < b);
		if(this.isTerminal) return this.value;
		else divideValue();
		if(i < left.b) return left.getValue(i);
		else return right.getValue(i);
	}
	
	// 配列に変換したもの（デバッグ用）
	T[] array(){
		T[] res;
		foreach(i; a .. b) res ~= this.value;
		return res;
	}
	
	// 文字列に変換したもの（デバッグ用）
	override string toString(){
		if(this.isTerminal) return this.array.to!string;
		else{
			string[] l = left.toString.split("\n"), r = right.toString.split("\n");
			while(l.length < r.length) l ~= l[$ - 1].map!(x => " ").join;
			while(l.length > r.length) r ~= r[$ - 1].map!(x => " ").join;
			string[] res = [this.array.to!string];
			foreach(i; 0 .. l.length) res ~= l[i] ~ r[i];
			return res.join("\n");
		}
	}
}

