#include <bits/stdc++.h>

#define rep(i,n) for(int i=0; i<(n); i++)
#define reps(i,x,n) for(int i=x; i<(n); i++)
#define rrep(i,n) for(int i=(n)-1; i>=0; i--)
#define all(X) (X).begin(),(X).end()
#define X first
#define Y second
#define pb push_back
#define eb emplace_back

using namespace std;
typedef long long int ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

template<class T> bool chmax(T &a, const T &b) { if (a<b) { a=b; return 1; } return 0; }
template<class T> bool chmin(T &a, const T &b) { if (a>b) { a=b; return 1; } return 0; }

template<class A, size_t N, class T> void Fill(A (&a)[N], const T &v){ fill( (T*)a, (T*)(a+N), v ); }

const ll INF = 1e9+7;


// 各ノードの情報
template <typename T>
struct node {
	T v, lazy;
	node(){
		v = INF;
		lazy = 0;
	}
	// ノードの値の更新
	void merge(T vl, T vr){
		v = min(vl, vr);
	}
	// ノードの値の更新(lazy適用)
	T add(int l, int r, T _v=0){
		T sum = lazy + _v;
		v += sum;
		lazy = 0;
		return sum;
	}
	// 遅延分の変更(加算)
	void addLazy(T v){
		lazy += v;
	}
};

template <typename T, typename NODE=node<T> >
class SegTree{
	// 遅延分の適応と伝播(現在のノードの値を確定させてLazyを伝搬させる)
	void propLazy(int a, int b, int k, T v=0){
		T lazy = data[k].add(a, b, v);
		if( k < N ){
			data[k*2+0].addLazy(lazy);
			data[k*2+1].addLazy(lazy);
		}
	}
	// セグメントツリーをたどる
	T prop(int l, int r, T v, int a, int b, int k=1){
		// 今いるノードの値を確定させる
		propLazy(a, b, k);
		// たどる
		NODE nd;	// 結果保存用(範囲外の場合は初期値が返る)
		if( l <= a && b <= r ){
			// 完全に範囲内の場合
			propLazy(a, b, k, v);
			return data[k].v;
		}else if( l < b && a < r ){
			// 中途半端に含まれる場合
			int m = (a+b) / 2;
			T vl = prop(l,r,v, a, m, k*2);
			T vr = prop(l,r,v, m, b, k*2+1);
			data[k].merge( data[k*2].v, data[k*2+1].v );
			nd.merge( vl, vr );
		}
		return nd.v;
	}

	public:
	vector<NODE> data;
	int N;
	SegTree(int _N){
		// _N <= (N = 2^k) を満たすNを見つける
		N = 1;
		while( N < _N ) N<<=1;
		data.resize(N*2);
	}
	// セグメントツリーの更新(指定した場所の値をvに変更)
	void update(int l, T v){
		prop(l, l+1, v-get(l), 0, N);
	}
	// セグメントツリーの更新(範囲に加算)
	T add(int l, int r, T v){
		return prop(l,r,v,0,N);
	}
	// 値を取得 [l,r)
	T get(int l, int r){
		return prop(l,r,0,0,N);
	}
	// 値を取得 [l,l+1)
	T get(int l){
		return prop(l,l+1,0,0,N);
	}
};


int main(){
	//ios_base::sync_with_stdio(0);
	int N, M, ans=0;
	vector<pii> v;

	cin >> N >> M;
	rep(i,N){
		int L, R;
		cin >> L >> R;
		v.eb(R, L);
	}

	// Rの値が大きい順にソート
	sort(all(v));
	reverse(all(v));

	// SegTreeで累積和を持つ
	// (i番目の要素が持つ値) = (iまでの席数) - (iまでのどこかに座りたい人の数)
	// この値が、負になってはいけない。
	SegTree<ll> sg(M+5);
	rep(i,M) sg.update(i+1, i+1);	// 初期値は席数の累積和

	// 左から順に座らせていく。
	vector<pii> late;
	for(auto t: v){
		int R = t.X;
		int L = t.Y;
		if( L > 0 && sg.get(L, M+2) > 0 ){
			// 席があれば座りたい。座りたい人が増えるので、それ以降のsgは1減る。
			sg.add(L, M+2, -1);
		}else{
			// うまく座れない場合は、右側に座るとして後回し。
			late.push_back( t );
		}
	}

	// Rの値が小さい順にソート
	sort(all(late));

	int pos = 0;  // 今見ている席
	for(auto t: late){
		int R = t.X;
		int L = t.Y;
		// posに座ることが可能であれば座る。
		while(true){
			if( pos <= M && R <= pos && sg.get(pos, M+2) > 0 ){
				// 座ることが可能
				sg.add(pos, M+2, -1);
				pos++;
			}else if( pos < M ){
				// 座れるまでposを進める
				pos++;
				continue;
			}else{
				// 座ることが不可能
				ans++;
			}
			break;
		}
	}

	cout << ans << endl;

	return 0;
}


Measured memory_bytes: 13040