#include <bits/stdc++.h>
#define rep(n) for(int I = 0; (I) < (n); ++(I) )
#define repeat(i, n) for( int i = 0; (i) < (n); ++(i) )
#define repeat_to(i, n) for( int i = 0; (i) <= (n); ++(i) )
#define repeat_from(i, m, n) for( int i = (m); (i) < (n); ++(i) )
#define repeat_from_to(i, m, n) for( int i = (m); (i) <= (n); ++(i) )
#define el cout<<endl
#define dump(x) cout<<" "<<#x<<"="<<x
#define vdump(v) for(size_t I=0; I<v.size(); ++I){cout<<" "<<#v<<"["<<I<<"]="<<v[I];} cout<<endl
using namespace std;
using lint = long long;
using ld = long double;

template <typename T>
struct lazy_propagation_segment_tree {
private:
    int n;
    vector<T> node, lazy;
    static const T INF = numeric_limits<T>::max() / 2;
    // 親 k なら子は 2k+1, 2k+2 , 子 k なら親は (k-1)/2
    // ノード数は 2n-1 で, 最後のノードは使わない
    // インデックス 0 から n-2 までが管理用, n-1 から 2n-2 までが実データ
public:
    lazy_propagation_segment_tree(int n_)
        : n(__lowest_power_of_2(n_)), node(n*2, 0), lazy(n*2, 0) {}
    
    template <typename Iterator>
    lazy_propagation_segment_tree(Iterator left, Iterator right)
        : n(__lowest_power_of_2(distance(left, right))), node(n * 2, 0), lazy(n * 2, 0) {    
        copy(left, right, node.begin() + n - 1);
        for (int i = n - 2; i >= 0; --i) {
            node[i] = std::max(node[i * 2 + 1], node[i * 2 + 2]);
        }
    }
    
    // 半開区間[ql, qr)に値xを一様に加算
    void add(int ql, int qr, const T &x) {__add(ql, qr, x, 0, 0, n);}
    
    // 半開区間[ql, qr)の最大値を求める
    T max(int ql, int qr) {return __max(ql, qr, 0, 0, n);}
    
private:
    void __flush_lazy(int k) {
        // 自ノード及び子ノードへの値の伝播を起こす
        node[k] += lazy[k];
        // 最下段かでなければ子ノードへ伝播
        if (k <= n-2) {
            lazy[2 * k + 1] += lazy[k];
            lazy[2 * k + 2] += lazy[k];
        }
        // 伝播が終わったので自ノードの遅延配列を空にする
        lazy[k] = 0;
    }
    
    // k は自分がいるノードのインデックスで, 
    // そのノードがカバーする区間を対象区間とよぶと, それが[l, r)
    void __add(const int ql, const int qr, const T &x, int k, int l, int r) {
        __flush_lazy(k);
        // 要求区間と対象区間が交わらない -> 何もしない
        if (r <= ql || qr <= l) return;
        // 要求区間が対象区間を完全に被覆 -> 遅延配列に値を入れた後に評価
        if (ql <= l && r <= qr) {
            lazy[k] += x;
            __flush_lazy(k);
        }
        // 要求区間が対象区間の一部を被覆 -> 子ノードを再帰的に計算
        else {
            __add(ql, qr, x, 2 * k + 1, l, (l+r) / 2);
            __add(ql, qr, x, 2 * k + 2, (l+r) / 2, r);
            node[k] = std::max(node[2 * k + 1], node[2 * k + 2]);
        }
    }       
    
    // k は自分がいるノードのインデックスで, 
    // そのノードがカバーする区間を対象区間とよぶと, それが[l, r)
    T __max(const int ql, const int qr, int k, int l, int r) {
        __flush_lazy(k);
        // 要求区間と対象区間が交わらない -> 答えに影響のない値を返す
        if (r <= ql || qr <= l) return - INF;
        // 要求区間が対象区間を完全に被覆 -> 対象区間の値を答えの計算に使う
        if (ql <= l && r <= qr) return node[k];
        // 要求区間が対象区間の一部を被覆 -> 子について探索を行う
        // 左側の子を vl, 右側の子を vr とする
        T vl = __max(ql, qr, 2 * k + 1, l, (l+r) / 2);
        T vr = __max(ql, qr, 2 * k + 2, (l+r) / 2, r);
        return std::max(vl, vr);
    }

    int __lowest_power_of_2(int x) const {
        int res = 1;
        while (res < x) res <<= 1;
        return res;
    }
    
public:
    void shownode(void) {vdump(node);}
    void showlazy(void) {vdump(lazy);}
};


int main(void) {
    int n, m;
    cin >> n >> m;
    vector<int> l(n), r(n);
    vector<pair<int, int>> lr(n);
    repeat(i, n) {
        cin >> l[i] >> r[i];
        lr[i] = {l[i], r[i]};
    }
    sort(lr.begin(), lr.end());
    
    lazy_propagation_segment_tree<int> st(m+2);
    repeat_from_to(i, 0, m+1) st.add(i, i+1, i);
    
    int ans = max(0, n-m);
    repeat(i, n) {
        st.add(0, lr[i].second + 1, 1);
        //cout << "now max " << st.max(0, m+2) << " and (l+m+1)=" << lr[i].first + m + 1 << endl;
        //st.shownode(); st.showlazy(); el;
        ans = max(ans, st.max(0, m+2) - (lr[i].first + m + 1));
    }
    cout << ans << endl;
}   

Measured memory_bytes: 7424