Problem:
Ayimok is a wizard.
His daily task is to arrange magical tiles in a line, and then cast a magic spell.
Magical tiles and their arrangement follow the rules below:
Each magical tile has the shape of a trapezoid with a height of 1.
Some magical tiles may overlap with other magical tiles.
Magical tiles are arranged on the 2D coordinate system.
Magical tiles' upper edge lay on a horizontal line, where its y coordinate is 1.
Magical tiles' lower edge lay on a horizontal line, where its y coordinate is 0.
He has to remove these magical tiles away after he finishes casting a magic spell.
One day, he found that removing a number of magical tiles is so tiresome, so he decided to simplify its process.
Now, he has learned "Magnetization Spell" to remove magical tiles efficiently. The spell removes a set of magical tiles at once. Any pair of two magical tiles in the set must overlap with each other.
For example, in Fig. 1, he can cast Magnetization Spell on all of three magical tiles to remove them away at once, since they all overlap with each other. (Note that the heights of magical tiles are intentionally changed for easier understandings.)
Fig. 1: Set of magical tiles which can be removed at once
However, in Fig. 2, he can not cast Magnetization Spell on all of three magical tiles at once, since tile 1 and tile 3 are not overlapped.
Fig. 2: Set of magical tiles which can NOT be removed at once
He wants to remove all the magical tiles with the minimum number of Magnetization Spells, because casting the spell requires magic power exhaustively.
Let's consider Fig. 3 case. He can remove all of magical tiles by casting Magnetization Spell three times; first spell for tile 1 and 2, second spell for tile 3 and 4, and third spell for tile 5 and 6.
Fig. 3: One way to remove all magical tiles (NOT the minimum number of spells casted)
Actually, he can remove all of the magical tiles with casting Magnetization Spell just twice; first spell for tile 1, 2 and 3, second spell for tile 4, 5 and 6.
And it is the minimum number of required spells for removing all magical tiles.
Fig. 4: The optimal way to remove all magical tiles(the minimum number of spells casted)
Given a set of magical tiles, your task is to create a program which outputs the minimum number of casting Magnetization Spell to remove all of these magical tiles away.
There might be a magical tile which does not overlap with other tiles, however, he still needs to cast Magnetization Spell to remove it.
Input
Input follows the following format:
N
xLower_{11}
xLower_{12}
xUpper_{11}
xUpper_{12}
xLower_{21}
xLower_{22}
xUpper_{21}
xUpper_{22}
xLower_{31}
xLower_{32}
xUpper_{31}
xUpper_{32}
:
:
xLower_{N1}
xLower_{N2}
xUpper_{N1}
xUpper_{N2}
The first line contains an integer
N
, which means the number of magical tiles.
Then,
N
lines which contain the information of magical tiles follow.
The
(i+1)
-th line includes four integers
xLower_{i1}
,
xLower_{i2}
,
xUpper_{i1}
, and
xUpper_{i2}
, which means
i
-th magica
...(truncated)

Submitted solutions:

code_snippet: // AOJ #2622 Removing Magical Tiles
// 2025.3.26

#include <bits/stdc++.h>
using namespace std;

#define gc() getchar_unlocked()
#define pc(c) putchar_unlocked(c)

int Cin() {  // 整数の入力
	int n = 0; int c = gc();
	if (c == '-') {	c = gc();
		do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
		return -n;
	}
	do n = 10*n + (c & 0xf), c = gc(); while (c >= '0');
	return n;
}

void Cout(int n) { // 整数の表示（出力）
	char b[30];
	if (!n) pc('0');
	else {
		if (n < 0) pc('-'), n = -n;
		int i = 0; while (n) b[i++] = n % 10 + '0', n /= 10;
		while (i--) pc(b[i]);
	}
	pc('\n');
}

const int SMAX = 262144;
vector<int> seg(2 * SMAX, 0);

int query(int L, int R, int qL, int qR, int idx) {
    if (qR < L || R < qL) return 0;
    if (qL <= L && R <= qR) return seg[idx];
    int mid = (L + R) / 2;
    return max(query(L, mid, qL, qR, idx * 2),
               query(mid + 1, R, qL, qR, idx * 2 + 1));
}

void update(int pos, int val) {
    pos += SMAX;
    while (pos > 0) {
        seg[pos] = max(seg[pos], val);
        pos /= 2;
    }
}

int main() {
    int n = Cin();

    vector<int> l1(n), r1(n), l2(n), r2(n);
    vector<int> z(2 * n);
    vector<pair<int, pair<int, int>>> ev(2 * n);

    for (int i = 0; i < n; i++) {
        l1[i] = Cin(), r1[i] = Cin(), l2[i] = Cin(), r2[i] = Cin();
        z[2 * i]     = r2[i];
        z[2 * i + 1] = l2[i];
        ev[2 * i]     = make_pair(l1[i], make_pair(i, 0));
        ev[2 * i + 1] = make_pair(r1[i], make_pair(i, 1));
    }

    sort(z.begin(), z.end());
    sort(ev.begin(), ev.end());

    vector<int> dp(n, 0);

    for (int i = 0; i < 2 * n; i++) {
        int id = ev[i].second.first;
        int type = ev[i].second.second;
        if (type == 0) {
            int pos = lower_bound(z.begin(), z.end(), l2[id]) - z.begin();
            dp[id] = query(0, SMAX - 1, 0, pos, 1) + 1;
        } else {
            int pos = lower_bound(z.begin(), z.end(), r2[id]) - z.begin();
            update(pos, dp[id]);
        }
    }

    int ans = 0;
    for (int i = 0; i < n; i++) ans = max(ans, dp[i]);
    Cout(ans);
    return 0;
}
accuracy: 1
time_ms: 50
memory_kb: 10152
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<b;i++)
using ll = long long;
#define all(p) p.begin(),p.end()

struct seg_tree{
	vector<int> val;
	int _n;
	seg_tree(int n){
		val.resize(n*2,0);
		_n=n;
	};
	void set(int ind,int v){
		ind+=_n;
		val[ind]=v;
		while(ind>>=1) val[ind]=max(val[ind<<1],val[(ind<<1)|1]);
	}
	int prod(int l,int r){
		int ans=0;
		for(l+=_n,r+=_n;l<r;l>>=1,r>>=1){
			if(l&1) ans=max(ans,val[l++]);
			if(r&1) ans=max(ans,val[--r]);
		}
		return ans;
	}
};

int main(){
	int n;
	cin>>n;
	vector<vector<int>> p(n,vector<int>(4));
	rep(i,0,n) rep(j,0,4) cin>>p[i][j];
	vector<pair<int,int>> order(n*2);
	vector<vector<int>> rev(n,vector<int>(4));
	rep(j,0,2){
		rep(i,0,n*2) order[i]={i%n,j*2+i/n};
		sort(all(order),[&](pair<int,int> l,pair<int,int> r){
			return p[l.first][l.second]<p[r.first][r.second];
		});
		rep(i,0,n*2) rev[order[i].first][order[i].second]=i;
	}
	vector<int> val(n);
	seg_tree seg(n*2);
	rep(i,0,n*2){
		int a,b;
		tie(a,b)=order[i];
		if(b==2){
			val[a]=seg.prod(0,rev[a][0])+1;
		}
		if(b==3){
			seg.set(rev[a][1],val[a]);
		}
	}
	int ans=0;
	for(auto x:val) ans=max(ans,x);
	cout<<ans<<"\n";
}
accuracy: 1
time_ms: 140
memory_kb: 17608
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<class T>bool chmax(T &a, const T &b) { if (a<b) { a=b; return true; } return false; }
template<class T>bool chmin(T &a, const T &b) { if (b<a) { a=b; return true; } return false; }
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define mp make_pair
#define si(x) int(x.size())
const int mod=998244353,MAX=300005,INF=1<<30;

// https://kopricky.github.io/code/SegmentTrees/rangetree_pointupdate.html

template<typename T> class segtree {
private:
    int n, sz;
    vector<T> node;
public:
    void init(const vector<T>& v){
        sz = (int)v.size();
        n = 1;
        while(n < sz){
            n *= 2;
        }
        node.assign(2*n, numeric_limits<T>::min());
        for(int i = 0; i < sz; i++){
            node[i+n] = v[i];
        }
        for(int i=n-1; i>=1; i--){
            node[i] = max(node[2*i], node[2*i+1]);
        }
    }
    void update(int k, const T a)
    {
        node[k+=n] = a;
        while(k>>=1){
            node[k] = max(node[2*k], node[2*k+1]);
        }
    }
    T query(int a, int b)
    {
        T res1 = numeric_limits<T>::min(), res2 = numeric_limits<T>::min();
        a += n, b += n;
        while(a != b){
            if(a % 2) res1 = max(res1, node[a++]);
            if(b % 2) res2 = max(res2, node[--b]);
            a >>= 1, b >>= 1;
        }
        return max(res1, res2);
    }
    void print(){
        for(int i = 0; i < sz; i++){
            cout << query(i, i+1) << " ";
        }
        cout << endl;
    }
};

//座標の型, 値の型
template<typename CandidateType, typename ValueType> class RangeTree
{
public:
    static_assert(std::is_integral<CandidateType>::value, "Integral required.");
private:
    using CT = CandidateType;
    using VT = ValueType;
    using pcc = pair<CT, CT>;
    using pci = pair<CT, int>;
    int n, sz;
    vector<segtree<VT> > seg;
    // y座標, x座標
    vector<vector<pcc> > yx;
    // y座標, x座標
    vector<pcc> sorted;
    void update_(int id, const CT x, const CT y, const VT val) {
        id += n-1;
        const int yid = lower_bound(all(yx[id]), pcc(y, x)) - yx[id].begin();
        seg[id].update(yid, val);
        while(id > 0){
            id = (id - 1) / 2;
            const int yid = lower_bound(all(yx[id]), pcc(y, x)) - yx[id].begin();
            seg[id].update(yid, val);
        }
    }
    VT query(const int lxid, const int rxid, const CT ly, const CT ry, const int k, const int l, const int r) {
        if(r <= lxid || rxid <= l) return numeric_limits<VT>::min();
        if(lxid <= l && r <= rxid){
            const int lyid = lower_bound(all(yx[k]), pcc(ly, numeric_limits<CT>::min())) - yx[k].begin();
            const int ryid = upper_bound(all(yx[k]), pcc(ry, numeric_limits<CT>::min())) - yx[k].begin();
            return (lyid >= ryid) ? numeric_limits<VT>::min() : seg[k].query(lyid, ryid);
        }else{
            return max(query(lxid, rxid, ly, ry, 2*k+1, l, (l+r)/2), query(lxid, rxid, ly, ry, 2*k+2, (l+r)/2, r));
        }
    }
public:
    // 座標, 点の値
    RangeTree(const vector<pcc>& cand, const vector<VT>& val) : n(1), sz((int)cand.size()), sorted(sz){
        while(n < sz) n *= 2;
        for(int i = 0; i < sz; ++i){
            sorted[i] = {cand[i].first, i};
        }
        sort(all(sorted), [&](const pcc& a, const pcc& b){
            return (a.first == b.first) ? (cand[a.second].second < cand[b.second].second) : (a.first < b.first);
        });
        yx.resize(2*n-1), seg.resize(2*n-1);
        for(int i = 0; i < sz; ++i){
            yx[i+n-1] = {{sorted[i].second, sorted[i].first}};
            vector<VT> arg = {val[sorted[i].second]};
            seg[i+n-1].init(arg);
            sorted[i].second = cand[sorted[i].second].second;
        }
        for(int i = n-2; i >= 0; --i){
            yx[i].resize((int)yx[2*i+1].size() + (int)yx[2*i+2].size());
            if(yx[i].empty()) continue;
            merge(all(yx[2*i+1]), all(yx[2*i+2]), yx[i].begin(), [&](const pcc& a, const pcc& b){
                return (cand[a.first].second == cand[b.first].second)
                ? (a.second < b.second) : (cand[a.first].second < cand[b.first].second);
            });
            vector<VT> arg((int)yx[i].size());
            for(int j = 0; j < (int)yx[i].size(); ++j){
                arg[j] = val[yx[i][j].first];
            }
            seg[i].init(arg);
        }
        for(int i = 0; i < 2*n-1; ++i){
            for(pcc& e : yx[i]){
                e.first = cand[e.first].second;
            }
        }
    }
    // 点 (x,y) の更新を行う
    void update(const CT x, const CT y, const VT val){
        const int id = lower_bound(all(sorted), pcc(x, y)) - sorted.begin();
        return update_(id, x, y, val);
    }
    // [lx,rx) × [ly,ry) の長方形領域のクエリに答える
    VT query(const CT lx, const CT ly, const CT rx, const CT ry){
        const int lxid = lower_bound(all(sorted), pcc(lx, numeric_limits<CT>::min())) - sorted.begin();
        const int rxid = upper_bound(all(sorted), pcc(rx, numeric_limits<CT>::min())) - sorted.begin();
        return (lxid >= rxid) ? numeric_limits<VT>::min() : query(lxid, rxid, ly, ry, 0, 0, n);
    }
};


int main(){
    
    std::ifstream in("text.txt");
    std::cin.rdbuf(in.rdbuf());
    cin.tie(0);
    ios::sync_with_stdio(false);
    
    int N;cin>>N;
    vector<vector<int>> A(N,vector<int>(4));
    for(int i=0;i<N;i++){
        for(int j=0;j<4;j++) cin>>A[i][j];
    }
    sort(all(A),[](auto a,auto b){
        return a[1]<b[1];
    });
    
    vector<pair<int,int>> cand;
    vector<int> val;
    
    for(int i=0;i<N;i++){
        cand.push_back(mp(A[i][1],A[i][3]));
        val.push_back(0);
    }
    
    RangeTree<int,int> rt(cand,val);
    
    for(int i=0;i<N;i++){
        int z=rt.query(-INF,-INF,A[i][0],A[i][2]);
        chmax(z,0);
        z++;
        rt.update(A[i][1],A[i][3],z);
    }
    
    cout<<rt.query(-INF,-INF,INF,INF)<<endl;
    
}
accuracy: 1
time_ms: 330
memory_kb: 62192
final_rank: 11

code_snippet: #line 1 "f.cpp"
// cSpell:disable
/*	author:  Kite_kuma
	created: 2022.09.16 01:06:01 */

#line 2 "SPJ-Library/template/kuma.hpp"

#line 2 "SPJ-Library/template/basic_func.hpp"

#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>

template <class T0, class T1, class T2>
inline bool in_range(T0 x, T1 lef, T2 rig) {
	return ((lef <= x) && (x < rig));
}

template <class T>
inline bool chmin(T &a, T b) {
	if(a > b) {
		a = b;
		return true;
	}
	return false;
}
template <class T>
inline bool chmax(T &a, T b) {
	if(a < b) {
		a = b;
		return true;
	}
	return false;
}

void Yes(bool flag = true) { std::cout << (flag ? "Yes" : "No") << '\n'; }
void YES(bool flag = true) { std::cout << (flag ? "YES" : "NO") << '\n'; }

template <class T>
void drop(T answer) {
	std::cout << answer << '\n';
	exit(0);
}

template <class T>
void vout(std::vector<T> const &v, bool vertically = 0) {
	if(vertically) {
		for(auto const &a : v) {
			std::cout << a << '\n';
		}
		return;
	}
	for(auto it = v.begin(); it != v.end(); it++) {
		std::cout << (*it);
		if(it != v.end() - 1) {
			std::cout << ' ';
		}
	}
	std::cout << '\n';
	return;
}

inline void print() { std::cout << '\n'; }
template <class T>
inline void print(const T &x) {
	std::cout << x << '\n';
	return;
}
template <typename Head, typename... Tail>
void print(const Head &H, const Tail &... T) {
	std::cout << H << " ";
	print(T...);
}

template <class T>
void add(std::vector<T> &v, T value) {
	for(auto &a : v) a += value;
	return;
}

template <class T>
T greatest_lower_multiple(T x, T d) {
	if(d == 0) return 0;
	if(d < 0) d *= -1;
	T y = x % d;
	if(y < 0) y += d;
	return x - y;
}

template <class T>
T least_upper_multiple(T x, T d) {
	return -greatest_lower_multiple(-x, d);
}

// ceil(a / b);
template <class T>
T dup(T a, T b) {
	assert(b);
	if(b < 0) {
		a *= -1;
		b *= -1;
	}
	return least_upper_multiple(a, b) / b;
}

long long pow_ll(long long a, long long n) {
	assert(n >= 0LL);
	if(n == 0) return 1LL;
	if(a == 0) return 0LL;
	if(a == 1) return 1LL;
	if(a == -1) return (n & 1LL) ? -1LL : 1LL;
	long long res = 1;
	while(n > 1LL) {
		if(n & 1LL) res *= a;
		a *= a;
		n >>= 1;
	}
	return res * a;
}

long long modpow(long long a, long long n, long long mod) {	 // a^n mod
	assert(n >= 0 && mod);
	if(mod == 1) return 0LL;
	long long res = 1;
	a %= mod;
	while(n > 0) {
		if(n & 1) res = res * a % mod;
		a = a * a % mod;
		n >>= 1;
	}
	return res < 0 ? res + mod : res;
}

// return x which satisfies a * x % mod == gcd(a, mod)
// not (mod | a)
long long modinv(long long a, const long long mod) {
	long long b = mod, u = 1, v = 0;
	while(b) {
		long long t = a / b;
		a -= t * b;
		std::swap(a, b);
		u -= t * v;
		std::swap(u, v);
	}
	u %= mod;
	if(u < 0) u += mod;
	return u;
}

template <class T>
int lb(const std::vector<T> &a, const T x) {
	return (int)std::distance(a.begin(), std::lower_bound(a.begin(), a.end(), x));
}
template <class T>
int ub(const std::vector<T> &a, const T x) {
	return (int)std::distance(a.begin(), std::upper_bound(a.begin(), a.end(), x));
}
template <class T>
void unq_sort(std::vector<T> &a) {
	std::sort(a.begin(), a.end());
	a.erase(std::unique(a.begin(), a.end()), a.end());
}
template <class T>
std::vector<int> press(const std::vector<T> &a) {
	std::vector<T> vec = a;
	unq_sort(vec);
	std::vector<int> ret;
	for(const auto &v : a) ret.push_back(lb(vec, v));
	return ret;
}
#line 1 "SPJ-Library/template/header.hpp"
#include <bits/stdc++.h>
#line 2 "SPJ-Library/template/io.hpp"

#line 4 "SPJ-Library/template/io.hpp"

#line 8 "SPJ-Library/template/debug.hpp"

#line 3 "SPJ-Library/template/constants.hpp"

constexpr int inf = 1000'000'000;
constexpr long long INF = 1'000'000'000'000'000'000LL;
constexpr int mod_1000000007 = 1000000007;
constexpr int mod_998244353 = 998244353;
const long double pi = acosl(-1.);
constexpr int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
constexpr int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};
#line 10 "SPJ-Library/template/debug.hpp"

namespace viewer {

template <typename T, typename U>
void view(const std::pair<T, U> &p);

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p);

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p);

void view(const long long &e);

void view(const int &e);

template <typename T>
void view(const T &e);

template <typename T>
void view(const std::set<T> &s);

template <typename T>
void view(const std::unordered_set<T> &s);

template <typename T>
void view(const std::multiset<T> &s);

template <typename T>
void view(const std::unordered_multiset<T> &s);

template <typename T>
void view(const std::vector<T> &v);

template <typename T, std::size_t ary_size>
void view(const std::array<T, ary_size> &v);

template <typename T>
void view(const std::vector<std::vector<T>> &vv);

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v);

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v);

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v);

template <typename map_type>
void view_map_container(const map_type &m);

template <typename T, typename U>
void view(const std::map<T, U> &m);

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m);

template <typename container_type>
void view_container(const container_type &c, bool vertically = false) {
	typename container_type::const_iterator begin = c.begin();
	const typename container_type::const_iterator end = c.end();
	if(vertically) {
		std::cerr << "{\n";
		while(begin != end) {
			std::cerr << '\t';
			view(*(begin++));
			if(begin != end) std::cerr << ',';
			std::cerr << '\n';
		}
		std::cerr << '}';
		return;
	}
	std::cerr << "{ ";
	while(begin != end) {
		view(*(begin++));
		if(begin != end) std::cerr << ',';
		std::cerr << ' ';
	}
	std::cerr << '}';
}

template <typename T, typename U>
void view(const std::pair<T, U> &p) {
	std::cerr << '(';
	view(p.first);
	std::cerr << ", ";
	view(p.second);
	std::cerr << ')';
}

template <class T0, class T1, class T2>
void view(const std::tuple<T0, T1, T2> &p) {
	std::cerr << '(';
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ')';
}

template <class T0, class T1, class T2, class T3>
void view(const std::tuple<T0, T1, T2, T3> &p) {
	std::cerr << '(';
	view(std::get<0>(p));
	std::cerr << ", ";
	view(std::get<1>(p));
	std::cerr << ", ";
	view(std::get<2>(p));
	std::cerr << ", ";
	view(std::get<3>(p));
	std::cerr << ')';
}

void view(const long long &e) {
	if(e == INF)
		std::cerr << "INF";
	else if(e == -INF)
		std::cerr << "-INF";
	else
		std::cerr << e;
}

void view(const int &e) {
	if(e == inf)
		std::cerr << "inf";
	else if(e == -inf)
		std::cerr << "-inf";
	else
		std::cerr << e;
}

template <typename T>
void view(const T &e) {
	std::cerr << e;
}

template <typename T>
void view(const std::set<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::unordered_set<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::multiset<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::unordered_multiset<T> &s) {
	view_container(s);
}

template <typename T>
void view(const std::vector<T> &v) {
	view_container(v);
}

template <typename T, std::size_t ary_size>
void view(const std::array<T, ary_size> &v) {
	view_container(v);
}

template <typename T>
void view(const std::vector<std::vector<T>> &vv) {
	view_container(vv, true);
}

template <typename T, typename U>
void view(const std::vector<std::pair<T, U>> &v) {
	view_container(v, true);
}

template <class T0, class T1, class T2>
void view(const std::vector<std::tuple<T0, T1, T2>> &v) {
	view_container(v, true);
}

template <class T0, class T1, class T2, class T3>
void view(const std::vector<std::tuple<T0, T1, T2, T3>> &v) {
	view_container(v, true);
}

template <typename map_type>
void view_map_container(const map_type &m) {
	std::cerr << "{\n";
	for(typename map_type::const_iterator it = m.begin(); it != m.end(); it++) {
		std::cerr << "\t[";
		view(it->first);
		std::cerr << "] : ";
		view(it->second);
		std::cerr << '\n';
	}
	std::cerr << "}";
}

template <typename T, typename U>
void view(const std::map<T, U> &m) {
	view_map_container(m);
}

template <typename T, typename U>
void view(const std::unordered_map<T, U> &m) {
	view_map_container(m);
}

}  // namespace viewer

// when compiling : g++ foo.cpp -DLOCAL
#ifdef LOCAL
void debug_out() {}
template <typename T>
void debug_out(const T &x) {
	viewer::view(x);
}
template <typename Head, typename... Tail>
void debug_out(const Head &H, const Tail &... T) {
	viewer::view(H);
	std::cerr << ", ";
	debug_out(T...);
}
#define debug(...)                                                \
	do {                                                          \
		std::cerr << __LINE__ << " [" << #__VA_ARGS__ << "] : ["; \
		debug_out(__VA_ARGS__);                                   \
		std::cerr << "]\n";                                       \
	} while(0)
#else
#define debug(...) (void(0))
#endif
#line 2 "SPJ-Library/template/scanner.hpp"

#line 6 "SPJ-Library/template/scanner.hpp"

namespace scanner {
template <class T>
void scan(T &a) {
	std::cin >> a;
}

template <class T, class L>
void scan(std::pair<T, L> &p) {
	scan(p.first);
	scan(p.second);
}

template <class T0, class T1, class T2>
void scan(std::tuple<T0, T1, T2> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	scan(t0);
	scan(t1);
	scan(t2);
	p = std::make_tuple(t0, t1, t2);
}

template <class T0, class T1, class T2, class T3>
void scan(std::tuple<T0, T1, T2, T3> &p) {
	T0 t0;
	T1 t1;
	T2 t2;
	T3 t3;
	scan(t0);
	scan(t1);
	scan(t2);
	scan(t3);
	p = std::make_tuple(t0, t1, t2, t3);
}

template <class T>
void scan(std::vector<T> &a) {
	for(auto &i : a) scan(i);
}

void INPUT() {}
template <class Head, class... Tail>
void INPUT(Head &head, Tail &... tail) {
	scan(head);
	INPUT(tail...);
}
}  // namespace scanner
#line 7 "SPJ-Library/template/io.hpp"

#define VEC(type, name, size) \
	vector<type> name(size);  \
	scanner::INPUT(name)
#define VVEC(type, name, h, w)                     \
	vector<vector<type>> name(h, vector<type>(w)); \
	scanner::INPUT(name)
#define INT(...)     \
	int __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LL(...)            \
	long long __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define STR(...)        \
	string __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define CHR(...)      \
	char __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define DBL(...)        \
	double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define LD(...)              \
	long double __VA_ARGS__; \
	scanner::INPUT(__VA_ARGS__)
#define TPL3(type0, type1, type2, name)   \
	std::tuple<type0, type1, type2> name; \
	scanner::INPUT(name);
#define TPL4(type0, type1, type2, type3, name)   \
	std::tuple<type0, type1, type2, type3> name; \
	scanner::INPUT(name);

template <typename T1, typename T2>
std::istream &operator>>(std::istream &is, std::pair<T1, T2> &p) {
	is >> p.first >> p.second;
	return is;
}

template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &p) {
	os << p.first << ' ' << p.second;
	return os;
}

struct fast_io {
	fast_io() {
		std::ios::sync_with_stdio(false);
		std::cin.tie(nullptr);
		std::cout << std::fixed << std::setprecision(15);
		srand((unsigned)time(NULL));
	}
} fast_io_;
#line 2 "SPJ-Library/template/macros.hpp"

#define foa(s, v) for(auto &s : v)
#define all(v) (v).begin(), (v).end()
#define rall(v) (v).rbegin(), (v).rend()
#define pcnt(n) __builtin_popcountll((long long)n)

#define REPname(a, b, c, d, e, ...) e
#define rep(...) REPname(__VA_ARGS__, REP3, REP2, REP1, REP0)(__VA_ARGS__)
#define REP0(x) for(int Counter_in_rep_macro = 0; Counter_in_rep_macro < (x); ++Counter_in_rep_macro)
#define REP1(i, x) for(int i = 0; i < (x); ++i)
#define REP2(i, l, r) for(int i = (l); i < (r); ++i)
#define REP3(i, l, r, c) for(int i = (l); i < (r); i += (c))
#define DREPname(a, b, c, d, e, ...) e
#define drep(...) DREPname(__VA_ARGS__, DREP3, DREP2, DREP1)(__VA_ARGS__)
#define DREP1(i, x) for(int i = (x)-1; i >= 0; --i)
#define DREP2(i, l, r) for(int i = (r)-1; i >= (l); --i)
#define DREP3(i, l, r, c) for(int i = (r)-1; i >= (l); i -= (c))

using ll = long long;
using ld = long double;
using ull = unsigned long long;
using vi = std::vector<int>;
using vvi = std::vector<std::vector<int>>;
using vvvi = std::vector<std::vector<std::vector<int>>>;
using vll = std::vector<ll>;
using vvll = std::vector<vll>;
using vvvll = std::vector<vvll>;
template <class T = ll>
using V = std::vector<T>;
template <class T = ll>
using VV = V<V<T>>;
template <class T = ll>
using VVV = V<V<V<T>>>;
template <class T = ll>
using pqup = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <class T = ll>
using pqdn = std::priority_queue<T>;
#line 7 "SPJ-Library/template/kuma.hpp"

using namespace std;
#line 6 "f.cpp"

int main() {
	INT(n);
	vector<pair<ll, ll>> prs;
	map<ll, ll> lowers;
	rep(i, n) {
		LL(a, b, c, d);
		prs.emplace_back(a, c);
		prs.emplace_back(b, d);
		lowers[a] = b;
	}

	sort(all(prs));

	map<ll, int> res;

	vector<ll> memo(n + 10, INF);
	memo[0] = 0;

	for(auto [low, high] : prs) {
		if(res.count(low)) {  // right side of the tile
			chmin(memo[res[low]], high);
		} else {  // left side
			auto idx = lb(memo, high);
			res[lowers[low]] = idx;
		}
	}

	drop(lb(memo, INF) - 1);

	return 0;
}
accuracy: 1
time_ms: 110
memory_kb: 20160
final_rank: 7

code_snippet: #include <bits/stdc++.h>

#ifdef _MSC_VER
#  include <intrin.h>
#else
#  include <x86intrin.h>
#endif

#include <limits>
#include <type_traits>

namespace suisen {
// ! utility
template <typename ...Types>
using constraints_t = std::enable_if_t<std::conjunction_v<Types...>, std::nullptr_t>;
template <bool cond_v, typename Then, typename OrElse>
constexpr decltype(auto) constexpr_if(Then&& then, OrElse&& or_else) {
    if constexpr (cond_v) {
        return std::forward<Then>(then);
    } else {
        return std::forward<OrElse>(or_else);
    }
}

// ! function
template <typename ReturnType, typename Callable, typename ...Args>
using is_same_as_invoke_result = std::is_same<std::invoke_result_t<Callable, Args...>, ReturnType>;
template <typename F, typename T>
using is_uni_op = is_same_as_invoke_result<T, F, T>;
template <typename F, typename T>
using is_bin_op = is_same_as_invoke_result<T, F, T, T>;

template <typename Comparator, typename T>
using is_comparator = std::is_same<std::invoke_result_t<Comparator, T, T>, bool>;

// ! integral
template <typename T, typename = constraints_t<std::is_integral<T>>>
constexpr int bit_num = std::numeric_limits<std::make_unsigned_t<T>>::digits;
template <typename T, unsigned int n>
struct is_nbit { static constexpr bool value = bit_num<T> == n; };
template <typename T, unsigned int n>
static constexpr bool is_nbit_v = is_nbit<T, n>::value;

// ?
template <typename T>
struct safely_multipliable {};
template <>
struct safely_multipliable<int> { using type = long long; };
template <>
struct safely_multipliable<long long> { using type = __int128_t; };
template <>
struct safely_multipliable<unsigned int> { using type = unsigned long long; };
template <>
struct safely_multipliable<unsigned long int> { using type = __uint128_t; };
template <>
struct safely_multipliable<unsigned long long> { using type = __uint128_t; };
template <>
struct safely_multipliable<float> { using type = float; };
template <>
struct safely_multipliable<double> { using type = double; };
template <>
struct safely_multipliable<long double> { using type = long double; };
template <typename T>
using safely_multipliable_t = typename safely_multipliable<T>::type;

template <typename T, typename = void>
struct rec_value_type {
    using type = T;
};
template <typename T>
struct rec_value_type<T, std::void_t<typename T::value_type>> {
    using type = typename rec_value_type<typename T::value_type>::type;
};
template <typename T>
using rec_value_type_t = typename rec_value_type<T>::type;

} // namespace suisen

// ! type aliases
using i128 = __int128_t;
using u128 = __uint128_t;

template <typename T>
using pq_greater = std::priority_queue<T, std::vector<T>, std::greater<T>>;
template <typename T, typename U>
using umap = std::unordered_map<T, U>;

// ! macros (capital: internal macro)
#define OVERLOAD2(_1,_2,name,...) name
#define OVERLOAD3(_1,_2,_3,name,...) name
#define OVERLOAD4(_1,_2,_3,_4,name,...) name

#define REP4(i,l,r,s)  for(std::remove_reference_t<std::remove_const_t<decltype(r)>>i=(l);i<(r);i+=(s))
#define REP3(i,l,r)    REP4(i,l,r,1)
#define REP2(i,n)      REP3(i,0,n)
#define REPINF3(i,l,s) for(std::remove_reference_t<std::remove_const_t<decltype(l)>>i=(l);;i+=(s))
#define REPINF2(i,l)   REPINF3(i,l,1)
#define REPINF1(i)     REPINF2(i,0)
#define RREP4(i,l,r,s) for(std::remove_reference_t<std::remove_const_t<decltype(r)>>i=(l)+fld((r)-(l)-1,s)*(s);i>=(l);i-=(s))
#define RREP3(i,l,r)   RREP4(i,l,r,1)
#define RREP2(i,n)     RREP3(i,0,n)

#define rep(...)    OVERLOAD4(__VA_ARGS__, REP4   , REP3   , REP2   )(__VA_ARGS__)
#define rrep(...)   OVERLOAD4(__VA_ARGS__, RREP4  , RREP3  , RREP2  )(__VA_ARGS__)
#define repinf(...) OVERLOAD3(__VA_ARGS__, REPINF3, REPINF2, REPINF1)(__VA_ARGS__)

#define CAT_I(a, b) a##b
#define CAT(a, b) CAT_I(a, b)
#define UNIQVAR(tag) CAT(tag, __LINE__)
#define loop(n) for (std::remove_reference_t<std::remove_const_t<decltype(n)>> UNIQVAR(loop_variable) = n; UNIQVAR(loop_variable) --> 0;)

#define all(iterable) std::begin(iterable), std::end(iterable)
#define input(type, ...) type __VA_ARGS__; read(__VA_ARGS__)

#ifdef LOCAL
#  define debug(...) debug_internal(#__VA_ARGS__, __VA_ARGS__)

template <class T, class... Args>
void debug_internal(const char* s, T&& first, Args&&... args) {
    constexpr const char* prefix = "[\033[32mDEBUG\033[m] ";
    constexpr const char* open_brakets = sizeof...(args) == 0 ? "" : "(";
    constexpr const char* close_brakets = sizeof...(args) == 0 ? "" : ")";
    std::cerr << prefix << open_brakets << s << close_brakets << ": " << open_brakets << std::forward<T>(first);
    ((std::cerr << ", " << std::forward<Args>(args)), ...);
    std::cerr << close_brakets << "\n";
}

#else
#  define debug(...) void(0)
#endif

// ! I/O utilities

// __int128_t
std::ostream& operator<<(std::ostream& dest, __int128_t value) {
    std::ostream::sentry s(dest);
    if (s) {
        __uint128_t tmp = value < 0 ? -value : value;
        char buffer[128];
        char* d = std::end(buffer);
        do {
            --d;
            *d = "0123456789"[tmp % 10];
            tmp /= 10;
        } while (tmp != 0);
        if (value < 0) {
            --d;
            *d = '-';
        }
        int len = std::end(buffer) - d;
        if (dest.rdbuf()->sputn(d, len) != len) {
            dest.setstate(std::ios_base::badbit);
        }
    }
    return dest;
}
// __uint128_t
std::ostream& operator<<(std::ostream& dest, __uint128_t value) {
    std::ostream::sentry s(dest);
    if (s) {
        char buffer[128];
        char* d = std::end(buffer);
        do {
            --d;
            *d = "0123456789"[value % 10];
            value /= 10;
        } while (value != 0);
        int len = std::end(buffer) - d;
        if (dest.rdbuf()->sputn(d, len) != len) {
            dest.setstate(std::ios_base::badbit);
        }
    }
    return dest;
}

// pair
template <typename T, typename U>
std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& a) {
    return out << a.first << ' ' << a.second;
}
// tuple
template <unsigned int N = 0, typename ...Args>
std::ostream& operator<<(std::ostream& out, const std::tuple<Args...>& a) {
    if constexpr (N >= std::tuple_size_v<std::tuple<Args...>>) {
        return out;
    } else {
        out << std::get<N>(a);
        if constexpr (N + 1 < std::tuple_size_v<std::tuple<Args...>>) {
            out << ' ';
        }
        return operator<<<N + 1>(out, a);
    }
}
// vector
template <typename T>
std::ostream& operator<<(std::ostream& out, const std::vector<T>& a) {
    for (auto it = a.begin(); it != a.end();) {
        out << *it;
        if (++it != a.end()) out << ' ';
    }
    return out;
}
// array
template <typename T, size_t N>
std::ostream& operator<<(std::ostream& out, const std::array<T, N>& a) {
    for (auto it = a.begin(); it != a.end();) {
        out << *it;
        if (++it != a.end()) out << ' ';
    }
    return out;
}
inline void print() { std::cout << '\n'; }
template <typename Head, typename... Tail>
inline void print(const Head& head, const Tail &...tails) {
    std::cout << head;
    if (sizeof...(tails)) std::cout << ' ';
    print(tails...);
}
template <typename Iterable>
auto print_all(const Iterable& v, std::string sep = " ", std::string end = "\n") -> decltype(std::cout << *v.begin(), void()) {
    for (auto it = v.begin(); it != v.end();) {
        std::cout << *it;
        if (++it != v.end()) std::cout << sep;
    }
    std::cout << end;
}

__int128_t parse_i128(std::string& s) {
    __int128_t ret = 0;
    for (int i = 0; i < int(s.size()); i++) if ('0' <= s[i] and s[i] <= '9') ret = 10 * ret + s[i] - '0';
    if (s[0] == '-') ret = -ret;
    return ret;
}
__uint128_t parse_u128(std::string& s) {
    __uint128_t ret = 0;
    for (int i = 0; i < int(s.size()); i++) if ('0' <= s[i] and s[i] <= '9') ret = 10 * ret + s[i] - '0';
    return ret;
}
// __int128_t
std::istream& operator>>(std::istream& in, __int128_t& v) {
    std::string s;
    in >> s;
    v = parse_i128(s);
    return in;
}
// __uint128_t
std::istream& operator>>(std::istream& in, __uint128_t& v) {
    std::string s;
    in >> s;
    v = parse_u128(s);
    return in;
}
// pair
template <typename T, typename U>
std::istream& operator>>(std::istream& in, std::pair<T, U>& a) {
    return in >> a.first >> a.second;
}
// tuple
template <unsigned int N = 0, typename ...Args>
std::istream& operator>>(std::istream& in, std::tuple<Args...>& a) {
    if constexpr (N >= std::tuple_size_v<std::tuple<Args...>>) {
        return in;
    } else {
        return operator>><N + 1>(in >> std::get<N>(a), a);
    }
}
// vector
template <typename T>
std::istream& operator>>(std::istream& in, std::vector<T>& a) {
    for (auto it = a.begin(); it != a.end(); ++it) in >> *it;
    return in;
}
// array
template <typename T, size_t N>
std::istream& operator>>(std::istream& in, std::array<T, N>& a) {
    for (auto it = a.begin(); it != a.end(); ++it) in >> *it;
    return in;
}
template <typename ...Args>
void read(Args &...args) {
    (std::cin >> ... >> args);
}

// ! integral utilities

// Returns pow(-1, n)
template <typename T>
constexpr inline int pow_m1(T n) {
    return -(n & 1) | 1;
}
// Returns pow(-1, n)
template <>
constexpr inline int pow_m1<bool>(bool n) {
    return -int(n) | 1;
}

// Returns floor(x / y)
template <typename T>
constexpr inline T fld(const T x, const T y) {
    return (x ^ y) >= 0 ? x / y : (x - (y + pow_m1(y >= 0))) / y;
}
template <typename T>
constexpr inline T cld(const T x, const T y) {
    return (x ^ y) <= 0 ? x / y : (x + (y + pow_m1(y >= 0))) / y;
}

template <typename T, suisen::constraints_t<suisen::is_nbit<T, 16>> = nullptr>
__attribute__((target("popcnt"))) constexpr inline int popcount(const T x) { return _mm_popcnt_u32(x); }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 32>> = nullptr>
__attribute__((target("popcnt"))) constexpr inline int popcount(const T x) { return _mm_popcnt_u32(x); }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 64>> = nullptr>
__attribute__((target("popcnt"))) constexpr inline int popcount(const T x) { return _mm_popcnt_u64(x); }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 16>> = nullptr>
constexpr inline int count_lz(const T x) { return x ? __builtin_clz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 32>> = nullptr>
constexpr inline int count_lz(const T x) { return x ? __builtin_clz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 64>> = nullptr>
constexpr inline int count_lz(const T x) { return x ? __builtin_clzll(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 16>> = nullptr>
constexpr inline int count_tz(const T x) { return x ? __builtin_ctz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 32>> = nullptr>
constexpr inline int count_tz(const T x) { return x ? __builtin_ctz(x) : suisen::bit_num<T>; }
template <typename T, suisen::constraints_t<suisen::is_nbit<T, 64>> = nullptr>
constexpr inline int count_tz(const T x) { return x ? __builtin_ctzll(x) : suisen::bit_num<T>; }
template <typename T>
constexpr inline int floor_log2(const T x) { return suisen::bit_num<T> -1 - count_lz(x); }
template <typename T>
constexpr inline int ceil_log2(const T x) { return floor_log2(x) + ((x & -x) != x); }
template <typename T>
constexpr inline int kth_bit(const T x, const unsigned int k) { return (x >> k) & 1; }
template <typename T>
constexpr inline int parity(const T x) { return popcount(x) & 1; }

// ! container

template <typename T, typename Comparator, suisen::constraints_t<suisen::is_comparator<Comparator, T>> = nullptr>
auto priqueue_comp(const Comparator comparator) {
    return std::priority_queue<T, std::vector<T>, Comparator>(comparator);
}

template <typename Iterable>
auto isize(const Iterable& iterable) -> decltype(int(iterable.size())) {
    return iterable.size();
}

template <typename T, typename Gen, suisen::constraints_t<suisen::is_same_as_invoke_result<T, Gen, int>> = nullptr>
auto generate_vector(int n, Gen generator) {
    std::vector<T> v(n);
    for (int i = 0; i < n; ++i) v[i] = generator(i);
    return v;
}
template <typename T>
auto generate_range_vector(T l, T r) {
    return generate_vector(r - l, [l](int i) { return l + i; });
}
template <typename T>
auto generate_range_vector(T n) {
    return generate_range_vector(0, n);
}

template <typename T>
void sort_unique_erase(std::vector<T>& a) {
    std::sort(a.begin(), a.end());
    a.erase(std::unique(a.begin(), a.end()), a.end());
}

template <typename InputIterator, typename BiConsumer>
auto foreach_adjacent_values(InputIterator first, InputIterator last, BiConsumer f) -> decltype(f(*first++, *last), void()) {
    if (first != last) for (auto itr = first, itl = itr++; itr != last; itl = itr++) f(*itl, *itr);
}
template <typename Container, typename BiConsumer>
auto foreach_adjacent_values(Container c, BiConsumer f) -> decltype(c.begin(), c.end(), void()) {
    foreach_adjacent_values(c.begin(), c.end(), f);
}

// ! other utilities

// x <- min(x, y). returns true iff `x` has chenged.
template <typename T>
inline bool chmin(T& x, const T& y) {
    if (y >= x) return false;
    x = y;
    return true;
}
// x <- max(x, y). returns true iff `x` has chenged.
template <typename T>
inline bool chmax(T& x, const T& y) {
    if (y <= x) return false;
    x = y;
    return true;
}

template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
std::string bin(T val, int bit_num = -1) {
    std::string res;
    if (bit_num >= 0) {
        for (int bit = bit_num; bit-- > 0;) res += '0' + ((val >> bit) & 1);
    } else {
        for (; val; val >>= 1) res += '0' + (val & 1);
        std::reverse(res.begin(), res.end());
    }
    return res;
}

template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
std::vector<T> digits_low_to_high(T val, T base = 10) {
    std::vector<T> res;
    for (; val; val /= base) res.push_back(val % base);
    if (res.empty()) res.push_back(T{ 0 });
    return res;
}
template <typename T, std::enable_if_t<std::is_integral_v<T>, std::nullptr_t> = nullptr>
std::vector<T> digits_high_to_low(T val, T base = 10) {
    auto res = digits_low_to_high(val, base);
    std::reverse(res.begin(), res.end());
    return res;
}

template <typename T>
std::string join(const std::vector<T>& v, const std::string& sep, const std::string& end) {
    std::ostringstream ss;
    for (auto it = v.begin(); it != v.end();) {
        ss << *it;
        if (++it != v.end()) ss << sep;
    }
    ss << end;
    return ss.str();
}

namespace suisen {}
using namespace suisen;
using namespace std;

struct io_setup {
    io_setup(int precision = 20) {
        std::ios::sync_with_stdio(false);
        std::cin.tie(nullptr);
        std::cout << std::fixed << std::setprecision(precision);
    }
} io_setup_ {};

// ! code from here

namespace suisen {
    template <typename T>
    struct FenwickTree {
        int n;
        vector<T> data;

        FenwickTree() : FenwickTree(0) {}
        FenwickTree(int n) : n(n), data(n, 0) {}
        int size() {
            return n;
        }
        void chmax(int i, T v) {
            for (++i; i <= n; i += (i & -i)) ::chmax(data[i - 1], v);
        }
        T max(int r) {
            T s = 0;
            for (; r; r -= r & -r) ::chmax(s, data[r - 1]);
            return s;
        }
    };
} // namespace suisen

namespace suisen {
    template <typename T>
    struct FenwickTree2DSparse {
        FenwickTree2DSparse() {}
        explicit FenwickTree2DSparse(int x_num) : n(x_num + 1), data(n), points(), pos_x(), pos_y(n) {}

        void add_point(int x, int y) {
            built = false;
            pos_x.push_back(x);
            points.emplace_back(x, y);
        }

        void build() {
            static constexpr int inf = std::numeric_limits<int>::max();
            built = true;
            pos_x.push_back(inf);
            std::sort(pos_x.begin(), pos_x.end());
            pos_x.erase(std::unique(pos_x.begin(), pos_x.end()), pos_x.end());
            assert(int(pos_x.size()) <= n);
            for (const auto& [x, y] : points) for (int k = comp_x(x) + 1; k <= n; k += k & -k) {
                pos_y[k - 1].push_back(y);
            }
            for (int k = 0; k < n; ++k) {
                pos_y[k].push_back(inf);
                std::sort(pos_y[k].begin(), pos_y[k].end());
                pos_y[k].erase(std::unique(pos_y[k].begin(), pos_y[k].end()), pos_y[k].end());
                data[k] = FenwickTree<T>(pos_y[k].size());
            }
        }

        void chmax(int x, int y, const T& val) {
            for (int k = comp_x(x) + 1; k <= n; k += k & -k) {
                data[k - 1].chmax(comp_y(k - 1, y), val);
            }
        }
        T max(int r, int u) {
            T res = 0;
            for (r = comp_x(r); r; r -= r & -r) ::chmax(res, data[r - 1].max(comp_y(r - 1, u)));
            return res;
        }

    private:
        int n, m;
        std::vector<FenwickTree<T>> data;
        std::vector<std::pair<int, int>> points;
        std::vector<int> pos_x;
        std::vector<std::vector<int>> pos_y;
        bool built = true;

        int comp_x(int x) const {
            return std::lower_bound(pos_x.begin(), pos_x.end(), x) - pos_x.begin();
        }
        int comp_y(int k, int y) const {
            return std::lower_bound(pos_y[k].begin(), pos_y[k].end(), y) - pos_y[k].begin();
        }
    };
} // namespace suisen

int main() {
    input(int, n);

    vector<tuple<int, int, int, int>> items(n);
    read(items);
    sort(all(items));

    FenwickTree2DSparse<int> ft(n);

    for (auto [dl, dr, ul, ur] : items) {
        ft.add_point(dr, ur);
    }
    ft.build();

    for (auto [dl, dr, ul, ur] : items) {
        ft.chmax(dr, ur, ft.max(dl, ul) + 1);
    }

    print(ft.max(numeric_limits<int>::max(), numeric_limits<int>::max()));

    return 0;
}
accuracy: 1
time_ms: 190
memory_kb: 24624
final_rank: 10

code_snippet: #pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
#include<iostream>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<queue>
#include<ciso646>
#include<random>
#include<map>
#include<set>
#include<bitset>
#include<stack>
#include<unordered_map>
#include<unordered_set>
#include<utility>
#include<cassert>
#include<complex>
#include<numeric>
#include<array>
#include<chrono>
using namespace std;

//#define int long long
typedef long long ll;

typedef unsigned long long ul;
typedef unsigned int ui;
constexpr ll mod = 998244353;
const ll INF = mod * mod;
typedef pair<int, int>P;

#define rep(i,n) for(int i=0;i<n;i++)
#define per(i,n) for(int i=n-1;i>=0;i--)
#define Rep(i,sta,n) for(int i=sta;i<n;i++)
#define rep1(i,n) for(int i=1;i<=n;i++)
#define per1(i,n) for(int i=n;i>=1;i--)
#define Rep1(i,sta,n) for(int i=sta;i<=n;i++)
#define all(v) (v).begin(),(v).end()
typedef pair<ll, ll> LP;

template<typename T>
void chmin(T& a, T b) {
	a = min(a, b);
}
template<typename T>
void chmax(T& a, T b) {
	a = max(a, b);
}
template<typename T>
void cinarray(vector<T>& v) {
	rep(i, v.size())cin >> v[i];
}
template<typename T>
void coutarray(vector<T>& v) {
	rep(i, v.size()) {
		if (i > 0)cout << " "; cout << v[i];
	}
	cout << "\n";
}
ll mod_pow(ll x, ll n, ll m = mod) {
	if (n < 0) {
		ll res = mod_pow(x, -n, m);
		return mod_pow(res, m - 2, m);
	}
	if (abs(x) >= m)x %= m;
	if (x < 0)x += m;
	if (x == 0)return 0;
	ll res = 1;
	while (n) {
		if (n & 1)res = res * x % m;
		x = x * x % m; n >>= 1;
	}
	return res;
}
struct modint {
	int n;
	modint() :n(0) { ; }
	modint(ll m) {
		if (m < 0 || mod <= m) {
			m %= mod; if (m < 0)m += mod;
		}
		n = m;
	}
	operator int() { return n; }
};
bool operator==(modint a, modint b) { return a.n == b.n; }
modint operator+=(modint& a, modint b) { a.n += b.n; if (a.n >= mod)a.n -= mod; return a; }
modint operator-=(modint& a, modint b) { a.n -= b.n; if (a.n < 0)a.n += mod; return a; }
modint operator*=(modint& a, modint b) { a.n = ((ll)a.n * b.n) % mod; return a; }
modint operator+(modint a, modint b) { return a += b; }
modint operator-(modint a, modint b) { return a -= b; }
modint operator*(modint a, modint b) { return a *= b; }
modint operator^(modint a, ll n) {
	if (n == 0)return modint(1);
	modint res = (a * a) ^ (n / 2);
	if (n % 2)res = res * a;
	return res;
}

ll inv(ll a, ll p) {
	return (a == 1 ? 1 : (1 - p * inv(p % a, a)) / a + p);
}
modint operator/(modint a, modint b) { return a * modint(inv(b, mod)); }
modint operator/=(modint& a, modint b) { a = a / b; return a; }
const int max_n = 1 << 20;
modint fact[max_n], factinv[max_n];
void init_f() {
	fact[0] = modint(1);
	for (int i = 0; i < max_n - 1; i++) {
		fact[i + 1] = fact[i] * modint(i + 1);
	}
	factinv[max_n - 1] = modint(1) / fact[max_n - 1];
	for (int i = max_n - 2; i >= 0; i--) {
		factinv[i] = factinv[i + 1] * modint(i + 1);
	}
}
modint comb(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[b] * factinv[a - b];
}
modint combP(int a, int b) {
	if (a < 0 || b < 0 || a < b)return 0;
	return fact[a] * factinv[a - b];
}

ll gcd(ll a, ll b) {
	a = abs(a); b = abs(b);
	if (a < b)swap(a, b);
	while (b) {
		ll r = a % b; a = b; b = r;
	}
	return a;
}
typedef long double ld;
typedef pair<ld, ld> LDP;
const ld eps = 1e-8;
const ld pi = acosl(-1.0);
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

using mP = pair<modint, modint>;
//-----------------------------------

template<typename T>
struct SegT {
private:
	int sz; vector<T> node;
	T init_c;
	function<T(T, T)> f;
public:
	SegT(vector<T> v, T _init_c, function<T(T, T)> _f) {
		init_c = _init_c; f = _f;
		int n = v.size();
		sz = 1;
		while (sz < n)sz *= 2;
		node.resize(2 * sz - 1, init_c);
		rep(i, n) {
			node[i + sz - 1] = v[i];
		}
		per(i, sz - 1) {
			node[i] = f(node[2 * i + 1], node[2 * i + 2]);
		}
	}
	SegT(int n, T _init_c, function<T(T, T)> _f) {
		init_c = _init_c; f = _f;
		sz = 1;
		while (sz < n)sz *= 2;
		node.resize(2 * sz - 1, init_c);
	}
	void update(int k, T a) {
		k += sz - 1;
		node[k] = f(node[k],a);
		while (k > 0) {
			k = (k - 1) / 2;
			node[k] = f(node[k * 2 + 1], node[k * 2 + 2]);
		}
	}
	T query(int a, int b, int k = 0, int l = 0, int r = -1) {
		if (r < 0)r = sz;
		if (r <= a || b <= l)return init_c;
		else if (a <= l && r <= b)return node[k];
		else {
			T vl = query(a, b, k * 2 + 1, l, (l + r) / 2);
			T vr = query(a, b, k * 2 + 2, (l + r) / 2, r);
			return f(vl, vr);
		}
	}
	//k以上でf(x,node[y+sz-1])をtrueにするような最小のy
	int searchloc(int le, T x, function<bool(T, T)> comp) {
		int  k = le + sz - 1;
		if (comp(x, node[k]))return le;
		x = f(x, node[k]);
		while (k > 0) {
			int mem = k;
			k = (k - 1) / 2;
			if (2 * k + 1 == mem) {
				if (comp(x, node[2 * k + 2])) {
					k = 2 * k + 2; break;
				}
				x = f(x, node[2 * k + 2]);
			}
		}
		if (k == 0)return sz;
		while (k < sz - 1) {
			if (comp(x, node[2 * k + 1])) {
				k = 2 * k + 1;
			}
			else {
				x = f(x, node[2 * k + 1]);
				k = 2 * k + 2;
			}
		}
		return k - (sz - 1);
	}
};
void solve() {
	int n; cin >> n;
	vector<int> lx(n), rx(n), ly(n), ry(n);
	rep(i, n)cin >> lx[i] >> rx[i] >> ly[i] >> ry[i];
	vector<int> vx, vy;
	rep(i, n) {
		vx.push_back(lx[i]); vx.push_back(rx[i]);
		vy.push_back(ly[i]); vy.push_back(ry[i]);
	}
	vy.push_back(-1);
	sort(all(vx)); vx.erase(unique(all(vx)), vx.end());
	sort(all(vy)); vy.erase(unique(all(vy)), vy.end());
	rep(i, n) {
		lx[i] = lower_bound(all(vx), lx[i]) - vx.begin();
		rx[i] = lower_bound(all(vx), rx[i]) - vx.begin();
		ly[i] = lower_bound(all(vy), ly[i]) - vy.begin();
		ry[i] = lower_bound(all(vy), ry[i]) - vy.begin();
	}
	vector<vector<int>> ids(vx.size());
	rep(i, n)ids[lx[i]].push_back(i);
	vector<vector<P>> ads(vx.size());
	auto f = [&](int a, int b) {return max(a, b); };
	SegT<int> st(vy.size(), -mod, f);
	st.update(0, 0);
	rep(i, vx.size()) {
		for (int id : ids[i]) {
			int val = st.query(0, ly[id]) + 1;
			ads[rx[id]].push_back({ ry[id],val });
		}
		for (P p : ads[i]) {
			st.update(p.first, p.second);
		}
	}
	int ans = st.query(0, vy.size());
	cout << ans << "\n";
}


signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	//cout << fixed << setprecision(10);
	//init_f();
	//init();
	//while(true)
	//expr();
	//int t; cin >> t; rep(i, t)
	solve();
	return 0;
}
accuracy: 1
time_ms: 120
memory_kb: 31908
final_rank: 9

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;



#define SIZE 100005
#define NUM 0

struct Info{

	int x_L1,x_L2,x_U1,x_U2;
};

struct Data{
	Data(int arg_value,int arg_index,bool arg_is_left){
		value = arg_value;
		index = arg_index;
		is_left = arg_is_left;
	}
	bool operator<(const struct Data &arg) const{

		return value < arg.value;
	}

	int value,index;
	bool is_left;
};

int N;
int table[8*SIZE];
int dp[SIZE];
Info info[SIZE];


void init(int first_N){
	while(N < first_N*2)N *= 2;
}

void update(int loc,int value){
	loc += N-1;

	table[loc] = value;

	if(N == 1)return;

	int parent = (loc-1)/2;

	while(true){
		table[parent] = max(table[2*parent+1],table[2*parent+2]);

		if(parent == 0)break;
		else{
			parent = (parent-1)/2;
		}
	}
}

int query(int search_left,int search_right,int node_id,int node_left,int node_right){

	if(search_right < node_left || search_left > node_right)return NUM;

	if(search_left <= node_left && search_right >= node_right){
		return table[node_id];
	}

	int left_max = query(search_left,search_right,2*node_id+1,node_left,(node_left+node_right)/2);
	int right_max = query(search_left,search_right,2*node_id+2,(node_left+node_right)/2+1,node_right);

	return max(left_max,right_max);
}


int main(){

	int first_N;

	scanf("%d",&first_N);

	vector<int> UPPER;
	vector<Data> V;

	for(int i = 0; i < first_N; i++){

		scanf("%d %d %d %d",&info[i].x_L1,&info[i].x_L2,&info[i].x_U1,&info[i].x_U2);

		//上辺
		UPPER.push_back(info[i].x_U1);
		UPPER.push_back(info[i].x_U2);

		//下辺
		V.push_back(Data(info[i].x_L1,i,true));
		V.push_back(Data(info[i].x_L2,i,false));
	}

	sort(UPPER.begin(),UPPER.end());
	sort(V.begin(),V.end());

	N = 1;
	init(first_N);

	for(int i = 0; i <= 2*N-2; i++){

		table[i] = 0;
	}

	for(int i = 0; i < first_N; i++){

		dp[i] = 0;
	}

	int L,R,mid;

	for(int i = 0; i < V.size(); i++){ //下辺の端点座標の昇順に走査

		int index = V[i].index;

		if(V[i].is_left){ //下辺左端の場合

			L = 0,R = UPPER.size()-1,mid = (L+R)/2;
			int loc;

			while(L <= R){

				if(info[index].x_U1 == UPPER[mid]){

					loc = mid;
					break;

				}else if(info[index].x_U1 > UPPER[mid]){

					L = mid+1;

				}else{

					R = mid-1;
				}
				mid = (L+R)/2;
			}

			dp[index] = query(0,loc-1,0,0,N-1)+1;

		}else{ //下辺右端の場合

			L = 0,R = UPPER.size()-1,mid = (L+R)/2;
			int loc;

			while(L <= R){

				if(info[index].x_U2 == UPPER[mid]){

					loc = mid;
					break;

				}else if(info[index].x_U2 > UPPER[mid]){

					L = mid+1;

				}else{

					R = mid-1;
				}
				mid = (L+R)/2;
			}

			update(loc,dp[index]);
		}
	}

	printf("%d\n",query(0,N-1,0,0,N-1));

	return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 10280
final_rank: 3

code_snippet: #include <cstring>
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>

#define LOWBIT( _x) ( (_x) & (-_x) )

using namespace std;

typedef pair<int ,int > pii;
typedef pair<int ,pii > pipii;
typedef vector<pipii > :: iterator vpipiii;
vector<pipii> L;
vector<int> U;

const int maxn = 100000 + 5;

int n;
int bit[ maxn << 1 ];
int l[ maxn ] , r[ maxn ] , f[ maxn ];

void insert(int p, int v)
{
	for (; p <= U.size() ; p += LOWBIT( p ) )
		bit[p] = max(bit[p] , v);
}

int query(int p)
{
	int ret = 0;
	for (; p ; p-= LOWBIT( p ) )
		ret = max(ret , bit[p]);
	return ret;
}

int main()
{
	int t1 , t2;
	scanf("%d",&n);
	for (int i = 0 ; i < n ; i++)
		scanf("%d%d%d%d", &t1, &t2, &l[i], &r[i]),
		L.push_back( pipii( t1 , pii( 0 , i ) ) ),
		L.push_back( pipii( t2 , pii( 1 , i ) ) ),
		U.push_back( l[i] ),
		U.push_back( r[i] );
	sort( U.begin() , U.end() );
	sort( L.begin() , L.end() );
	for (int i = 0 ; i < U.size() ; i++)
	{
		int id = L[i].second.second , flag = L[i].second.first;
		if ( flag )
		{
			insert( lower_bound( U.begin() , U.end() , r[id] ) - U.begin() + 1 , f[id]);
		}else
			f[id] = query( lower_bound( U.begin() , U.end() , l[id] ) - U.begin() + 1 ) + 1;
	}
	printf("%d\n", *max_element(&f[0] , &f[n]) );
	return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 7056
final_rank: 2

code_snippet: #include<stdio.h>
#include<algorithm>
using namespace std;
int l1[110000];
int r1[110000];
int l2[110000];
int r2[110000];
int segtree[524288];
int dp[110000];
int query(int a,int b,int c,int d,int e){
	if(d<a||b<c)return 0;
	if(c<=a&&b<=d)return segtree[e];
	return max(query(a,(a+b)/2,c,d,e*2),query((a+b)/2+1,b,c,d,e*2+1));
}
void update(int a,int b){
	a+=262144;
	while(a){
		segtree[a]=max(segtree[a],b);
		a/=2;
	}
}
pair<int,pair<int,int> >ev[210000];
int z[210000];
int main(){
	int a;
	scanf("%d",&a);
	for(int i=0;i<a;i++){
		scanf("%d%d%d%d",l1+i,r1+i,l2+i,r2+i);
		z[i*2]=r2[i];
		z[i*2+1]=l2[i];
		ev[i*2]=make_pair(l1[i],make_pair(i,0));
		ev[i*2+1]=make_pair(r1[i],make_pair(i,1));
	}
	std::sort(z,z+a*2);
	std::sort(ev,ev+a*2);
	for(int i=0;i<a*2;i++){
		int at=ev[i].second.first;
		if(ev[i].second.second==0){
			int pos=lower_bound(z,z+a*2,l2[at])-z;
			dp[at]=query(0,262143,0,pos,1)+1;
		}else{
			int pos=lower_bound(z,z+a*2,r2[at])-z;
			update(pos,dp[at]);
		}
	}
	int ret=0;
	for(int i=0;i<a;i++)ret=max(ret,dp[i]);
	printf("%d\n",ret);
}
accuracy: 1
time_ms: 100
memory_kb: 8332
final_rank: 5

code_snippet: #include<stdio.h>
#include<algorithm>
using namespace std;
int l1[110000];
int r1[110000];
int l2[110000];
int r2[110000];
int segtree[262144];
int dp[110000];
int query(int a,int b,int c,int d,int e){
	if(d<a||b<c)return 0;
	if(c<=a&&b<=d)return segtree[e];
	return max(query(a,(a+b)/2,c,d,e*2),query((a+b)/2+1,b,c,d,e*2+1));
}
void update(int a,int b){
	a+=131072;
	while(a){
		segtree[a]=max(segtree[a],b);
		a/=2;
	}
}
pair<int,pair<int,int> >ev[210000];
int z[210000];
int main(){
	int a;
	scanf("%d",&a);
	for(int i=0;i<a;i++){
		scanf("%d%d%d%d",l1+i,r1+i,l2+i,r2+i);
		z[i*2]=r2[i];
		z[i*2+1]=l2[i];
		ev[i*2]=make_pair(l1[i],make_pair(i,0));
		ev[i*2+1]=make_pair(r1[i],make_pair(i,1));
	}
	std::sort(z,z+a*2);
	std::sort(ev,ev+a*2);
	for(int i=0;i<a*2;i++){
		int at=ev[i].second.first;
		if(ev[i].second.second==0){
			int pos=lower_bound(z,z+a*2,l2[at])-z;
			dp[at]=query(0,131071,0,pos,1)+1;
		}else{
			int pos=lower_bound(z,z+a*2,r2[at])-z;
			update(pos,dp[at]);
		}
	}
	int ret=0;
	for(int i=0;i<a;i++)ret=max(ret,dp[i]);
	printf("%d\n",ret);
}
accuracy: 0.696969696969697
time_ms: 100
memory_kb: 7400
final_rank: 12

code_snippet: #include <cstdio>
#include <utility>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int,int> pi;

int n;
int a[100005], b[100005], c[100005], d[100005];
int dp[100005];

struct seg{int a,b,p,i;}st[200005];
bool cmp(seg a, seg b){return a.a < b.a;}

struct rmq{
    int lim, tree[530000];
    void init(int n){
        for(lim = 1; lim <= n; lim <<= 1);
    }
    void ins(int s, int v){
        s += lim;
        tree[s] = v;
        while(s>1){
            s >>= 1;
            tree[s] = max(tree[2*s],tree[2*s+1]);
        }
    }
    int q(int s, int e){
        s += lim;
        e += lim;
        int r = 0;
        while(s<e){
            if(s%2 == 1) r = max(r,tree[s++]);
            if(e%2 == 0) r = max(r,tree[e--]);
            s >>= 1;
            e >>= 1;
        }
        if(s == e) r = max(r,tree[s]);
        return r;
    }
}rmq;

void input(){
    vector<int> vu, vd;
    scanf("%d",&n);
    rmq.init(2*n);
    for (int i=0; i<n; i++) {
        scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
        vu.push_back(a[i]);
        vu.push_back(b[i]);
        vd.push_back(c[i]);
        vd.push_back(d[i]);
    }
    sort(vu.begin(),vu.end());
    sort(vd.begin(),vd.end());
    for (int i=0; i<n; i++) {
        a[i] = (int)(lower_bound(vu.begin(),vu.end(),a[i]) - vu.begin());
        b[i] = (int)(lower_bound(vu.begin(),vu.end(),b[i]) - vu.begin());
        c[i] = (int)(lower_bound(vd.begin(),vd.end(),c[i]) - vd.begin());
        d[i] = (int)(lower_bound(vd.begin(),vd.end(),d[i]) - vd.begin());
        st[2*i] = {a[i],c[i],0,i};
        st[2*i+1] = {b[i],d[i],1,i};
    }
    sort(st,st+2*n,cmp);
}

vector<pi> v;

int main(){
    input();
    for (int i=0; i<2*n; i++) {
        if(st[i].p == 0){
            dp[st[i].i] = rmq.q(0,st[i].b) + 1;
        }
        else{
            rmq.ins(st[i].b,dp[st[i].i]);
        }
    }
    int mcnt = *max_element(dp,dp+n);
    printf("%d\n",mcnt);
}
accuracy: 1
time_ms: 140
memory_kb: 7876
final_rank: 6

code_snippet: #include<cstdio>
#include<utility>
#include<algorithm>

using namespace std;

struct RMQ{
	int N;
	int dat[524288];
	void init(int N_){
		N=1;
		while(N<N_) N*=2;
		for(int i=1;i<=N*2-1;i++) dat[i]=-1;
	}
	void update(int i,int x){
		i+=N;
		while(i>0){
			dat[i]=max(dat[i],x);
			i/=2;
		}
	}
	int get(int i){//,i)
		int k=1,l=0,r=N;
		int res=-1;
		while(r-l>1){
			int mid=(l+r)/2;
			if(mid<i){
				res=max(res,dat[k*2]);
				k=k*2+1;
				l=mid;
			}else{
				r=mid;
				k*=2;
			}
		}
		return res;
	}
};

RMQ rmq;

int N;
int lo1[100100],lo2[100100],hi1[100100],hi2[100100];
int clo1[100100],clo2[100100];

int lo_vals[200200];

int getPos(int x){
	return lower_bound(lo_vals,lo_vals+N*2,x)-lo_vals;
}

void input(){
	scanf("%d",&N);
	for(int i=0;i<N;i++){
		scanf("%d%d%d%d",lo1+i,lo2+i,hi1+i,hi2+i);
		lo_vals[i*2]=lo1[i];
		lo_vals[i*2+1]=lo2[i];
	}
	sort(lo_vals,lo_vals+N*2);
	for(int i=0;i<N;i++){
		clo1[i]=getPos(lo1[i]);
		clo2[i]=getPos(lo2[i]);
	}
}

typedef pair<int,int> P;
typedef pair<P,P> PP;

PP pps[200200];

int ans[100100];

int solve(){
	for(int i=0;i<N;i++){
		PP pp=PP(P(hi1[i],clo1[i]),P(i,0));
		pps[i*2]=pp;
		pp=PP(P(hi2[i],clo2[i]),P(i,1));
		pps[i*2+1]=pp;
	}
	sort(pps,pps+N*2);
	rmq.init(N*2);
	for(int i=0;i<N*2;i++){
		PP pp=pps[i];
//		printf("%d %d\n",pps[i].first.first,pps[i].first.second);
		if(pp.second.second==0){
			int pos=pp.first.second;
			int val=rmq.get(pos+1);
			int id=pp.second.first;
			ans[id]=val+1;
//			printf("pos=%d,ans=%d\n",pos,val+1);
		}else{
			int id=pp.second.first;
			int val=ans[id];
			int pos=pp.first.second;
			rmq.update(pos,val);
//			printf("upd pos=%d,val=%d\n",pos,val);
		}
	}
	int res=0;
	for(int i=0;i<N;i++) res=max(res,ans[i]+1);
	return res;
}

int main(){
	input();
	int ans=solve();
	printf("%d\n",ans);
	return 0;
}
accuracy: 1
time_ms: 90
memory_kb: 9736
final_rank: 4