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

//#define int long long

struct T {
    static const int N = 4e5 + 7;
    int t[N << 2], lzy[N << 2];
    void push(int v, int l, int r) {
        if(lzy[v]) {
            t[v] += lzy[v];
            if(l != r) {
                lzy[v * 2] += lzy[v];
                lzy[v * 2 + 1] += lzy[v];
            }
            lzy[v] = 0;
        }
    }

    void modify(int v, int l, int r, int b, int e, int x) {
        push(v, l, r);
        if(l > e || r < b) {
            return;
        }
        if(l >= b && r <= e) {
            lzy[v] += x;
            push(v, l, r);
            return;
        }
        int mid = (l + r) >> 1;
        modify(v * 2, l, mid, b, e, x);
        modify(v * 2 + 1, mid + 1, r, b, e, x);
        t[v] = max(t[v * 2], t[v * 2 + 1]);

    }

    void update(int v, int l, int r, int p, int x) {
        push(v, l, r);
        if(l == r) {
            t[v] = x;
            return;
        }
        int mid = (l + r) >> 1;
        push(v * 2, l, mid);
        push(v * 2 + 1, mid + 1, r);
        if(p <= mid) update(v * 2, l, mid, p, x);
        else         update(v * 2 + 1, mid + 1, r, p, x);
        t[v] = max(t[v * 2], t[v * 2 + 1]);
    }

}W, B;

const int N = 4e5 + 7;

vector <pair <int, int> > Q[N];

int l[N], r[N], t[N], dp[N];

int32_t main() {
    ios_base :: sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int n; cin >> n;
    vector <int> v;
    for(int i = 1; i <= n; i++) {
        cin >> l[i] >> r[i] >> t[i];
        v.push_back(l[i]);
        v.push_back(r[i]);
    }

    sort(v.begin(), v.end()); map <int, int> mp; int ptr = 0;
    for(int x: v) if(!mp[x]) mp[x] = ++ptr;

    for(int i = 1; i <= n; i++) {
        Q[mp[l[i]]].push_back(make_pair(mp[r[i]], t[i]));
    }

    for(int i = ptr; i >= 1; i--) {
        for(auto z: Q[i]) {
            int rx, idx; tie(rx, idx) = z;
            if(idx == 1) B.modify(1, 1, ptr + 1, rx + 1, ptr + 1, 1);
            else         W.modify(1, 1, ptr + 1, rx + 1, ptr + 1, 1);
        }
        dp[i] = max({dp[i + 1], B.t[1], W.t[1]});
        B.update(1, 1, ptr + 1, i, dp[i]);
        W.update(1, 1, ptr + 1, i, dp[i]);
    }

    cout << *max_element(dp, dp + N) << endl;
}


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

//#define int long long

const int N = 4e5 + 7;

vector <int> t[2][N << 2];

vector <int> aa[2][N];

void build(int v, int l, int r) {
	if(l == r) {
		sort(aa[0][l].begin(), aa[0][l].end());
		sort(aa[1][l].begin(), aa[1][l].end());
		t[0][v] = aa[0][l];
		t[1][v] = aa[1][l];
		return;
	}
	int mid = (l + r) >> 1;
	build(v * 2, l, mid);
	build(v * 2 + 1, mid + 1, r);
	merge(t[0][v * 2].begin(), t[0][v * 2].end(), t[0][v * 2 + 1].begin(), t[0][v * 2 + 1].end(), back_inserter(t[0][v]));
	merge(t[1][v * 2].begin(), t[1][v * 2].end(), t[1][v * 2 + 1].begin(), t[1][v * 2 + 1].end(), back_inserter(t[1][v]));

}

int query(int v, int l, int r, int L, int R, int lx, int rx, int idx) {
	if(l > R || r < L || lx > rx) return 0;
	if(l >= L && r <= R) return upper_bound(t[idx][v].begin(), t[idx][v].end(), rx) - lower_bound(t[idx][v].begin(), t[idx][v].end(), lx);
	int mid = (l + r) >> 1;
	return query(v * 2, l, mid, L, R, lx, rx, idx) + query(v * 2 + 1, mid + 1, r, L, R, lx, rx, idx);
}

int l[N], r[N], qq[N], dp[N], dpp[N];

int32_t main() {
	ios_base :: sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n; cin >> n;
	vector <int> vv;
	for(int i = 1; i <= n; ++i) {
		cin >> l[i] >> r[i] >> qq[i]; qq[i]--;
		vv.push_back(l[i]);
		vv.push_back(r[i]);
	}

	sort(vv.begin(), vv.end());
	map <int, int> mp; int ptr = 0;
	for(int x: vv) if(!mp[x]) mp[x] = ++ptr;

	vector <tuple <int, int, int> > arr;

	for(int i = 1; i <= n; i++) {
		l[i] = mp[l[i]];
		r[i] = mp[r[i]];
		aa[qq[i]][l[i]].push_back(r[i]);
		arr.push_back(make_tuple(l[i], -r[i], qq[i]));
	}

	build(1, 1, ptr);

	sort(arr.begin(), arr.end());

	set <pair <int, int> > st[2];

	for(int i = n - 1; i >= 0; i--) {
		int lx, rx, qqx; tie(lx, rx, qqx) = arr[i]; rx *= -1;
		int z = query(1, 1, ptr, lx, rx, lx, rx, qqx);
		int nxt = lower_bound(arr.begin(), arr.end(), make_tuple(rx + 1, -1e9, 0)) - arr.begin();
		dpp[i] = z + dp[nxt];

		auto it = st[qqx].lower_bound(make_pair(rx + 1, 0));
		if(it != st[qqx].end()) {
			int idy, rr; tie(rr, idy) = *it;
			int ly, ry, qy; tie(ly, ry, qy) = arr[idy]; ry *= -1;
			dpp[i] = max(dpp[i], z - query(1, 1, ptr, ly, rx, ly, rx, qqx) + dpp[idy]);
		}
		st[qqx].insert(make_pair(rx, i));
		dp[i] = max(dp[i + 1], dpp[i]);
	//	cout << lx << " " << rx << " " << qqx << " " << dp[i] << " " << z << " " << dp[nxt] << endl;
	}

	cout << *max_element(dp, dp + N) << endl;
}


#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <queue>
#include <string>
#include <utility>
#include <cassert>
#include <functional>
#include <numeric>

using namespace std;
typedef long long ll;

int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);

  int n;
  cin >> n;
  vector<pair<int, int>> uno, dos;
  for (int i = 0; i < n; i++) {
    int t, l, r;
    cin >> l >> r >> t;
    if (t == 1) {
      uno.emplace_back(r, l);
    } else {
      dos.emplace_back(l, r);
    }
  }

  sort(uno.begin(), uno.end());
  sort(dos.begin(), dos.end());

  int match = 0;
  int idx = 0;
  set<pair<int, int>> st;
  for (auto p: uno) {
    int l = p.second;
    int r = p.first;
    while (idx < dos.size() && dos[idx].first <= r) {
      st.insert({dos[idx].second, idx});
      idx++;
    }
    auto it = st.lower_bound({l, -1});
    if (it != st.end()) {
      st.erase(it);
      match++;
    }
  }

  cout << n - match << endl;

  return 0;
}


#include <bits/stdc++.h>

using namespace std;

#ifdef NeverBeRed
#include "debug.h"
#else
#define debug(...) 9715
#endif
typedef long long ll;
typedef long double ld;
typedef complex<ll> point;
#define F first
#define S second

enum seg_type { seg_not_lazy, seg_lazy, seg_beats };
template<class node, seg_type type>
struct segment_tree
{
	using node_container = typename node::node_container;
	using lazy_container = typename node::lazy_container;
	#define enable_if(x) template <const bool T = (x), typename enable_if<T, bool>::type = 0>
	#define enable_if_not(x) template <const bool T = (x), typename enable_if<!T, bool>::type = 0>

private:
	enable_if(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m)
	{
		if (st[x].lazy())
		{
			st[x + 1].apply(b, m, st[x].lazy);
			st[x + ((m - b) << 1)].apply(m, e, st[x].lazy);
			st[x].lazy = lazy_container();
		}
	}

	enable_if_not(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m) {}

	template<typename RAIter>
	void build(int x, int b, int e, const RAIter &a)
	{
		if (b+1 == e)
		{
			st[x].nod.build(a[b]);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);

		build(x + 1, b, m, a);
		build(y, m, e, a);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if_not(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (st[x].break_condition(lazy)) return;

		if (lo <= b && e <= hi && st[x].tag_condition(lazy))
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	node_container query(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
			return st[x].nod;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m >= hi) return query(x + 1, b, m);
		if (m <= lo) return query(y, m, e);
		return query(x + 1, b, m) + query(y, m, e);
	}

	template<class P>
	int find_first(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m && f(st[x + 1]))
		{
			auto t = find_first(x + 1, b, m, f);
			if (t != -1) return t;
		}
		if (m < hi && f(st[y]))
			return find_first(y, m, e, f);
		return -1;
	}

	template<class P>
	int find_last(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m < hi && f(st[y]))
		{
			auto t = find_last(y, m, e, f);
			if (t != -1) return t;
		}
		if (lo < m && f(st[x + 1]))
			return find_last(x + 1, b, m, f);
		return -1;
	}

	lazy_container lazy;
	int n, lo, hi;
	vector<node> st;

public:
	template<typename RAIter>
	void build(const RAIter &a)
	{
		build(0, 0, n, a);
	}

	void update(int l, int r, const lazy_container &x)
	{
		lo = l, hi = r, lazy = x, update_(0, 0, n);
	}

	node_container query(int l, int r)
	{
		return lo = l, hi = r, query(0, 0, n);
	}

	template<class P>
	int find_first(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_first(0, 0, n, f);
	}

	template<class P>
	int find_last(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_last(0, 0, n, f);
	}

	segment_tree(int n) : n(n), st(2 * n - 1) {}
};

struct node
{
	struct node_container
	{
		int x;

		template<typename T>
		inline void build(const T &a) // build(leave) from a
		{
			x = a;
		}

		friend node_container operator+(node_container l, const node_container &r) // merge l and r
		{
			l.x = max(l.x, r.x);
			return l;
		}

		node_container() : x(0) {}
	} nod;

	struct lazy_container
	{
		int add;

		inline bool operator()() // has lazy
		{
			return add != 0;
		}

		lazy_container(ll add = 0) : add(add) {}
	} lazy;

	inline void apply(int l, int r, lazy_container &p) // apply lazy
	{
		nod.x += p.add;
		lazy.add += p.add;
	}
};

struct seg
{
	int l, r, c;
};

int main()
{
	#ifdef TurnRed
		//freopen("a.in", "r", stdin);
		//freopen("a.out", "w", stdout);
	#endif

	ios_base::sync_with_stdio(0), cin.tie(0);

	int n;
	cin >> n;
	vector<seg> a(n);
	vector<int> all;
	for (auto &i : a)
	{
		cin >> i.l >> i.r >> i.c;
		all.push_back(i.l);
		all.push_back(i.r);
	}

	auto get = [&](int x) { return lower_bound(all.begin(), all.end(), x) - all.begin() + 1; };
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	for (auto &i : a)
		i.l = get(i.l), i.r = get(i.r);

	const int sz = all.size() + 1;
	segment_tree<node, seg_lazy> st1(sz), st2(sz);
	vector<vector<int>> end(sz);
	for (auto &i : a)
		end[i.r].push_back(&i-&a[0]);

	for (int i = 1; i < sz; ++i)
	{
		for (auto j : end[i])
		{
			if (a[j].c == 1 && a[j].l > 0) st2.update(0, a[j].l, +1);
			if (a[j].c == 2 && a[j].l > 0) st1.update(0, a[j].l, +1);
		}

		int best = max(st1.query(0, i).x, st2.query(0, i).x);
		st1.update(i, i+1, best);
		st2.update(i, i+1, best);
	}

	cout << max(st1.query(0, sz).x, st2.query(0, sz).x) << "\n";

	return 0;
}


#include <bits/stdc++.h>

using namespace std;

#ifdef NeverBeRed
#include "debug.h"
#else
#define debug(...) 9715
#endif
typedef long long ll;
typedef long double ld;
typedef complex<ll> point;
#define F first
#define S second

enum seg_type { seg_not_lazy, seg_lazy, seg_beats };
template<class node, seg_type type>
struct segment_tree
{
	using node_container = typename node::node_container;
	using lazy_container = typename node::lazy_container;
	#define enable_if(x) template <const bool T = (x), typename enable_if<T, bool>::type = 0>
	#define enable_if_not(x) template <const bool T = (x), typename enable_if<!T, bool>::type = 0>

private:
	enable_if(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m)
	{
		if (st[x].lazy())
		{
			st[x + 1].apply(b, m, st[x].lazy);
			st[x + ((m - b) << 1)].apply(m, e, st[x].lazy);
			st[x].lazy = lazy_container();
		}
	}

	enable_if_not(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m) {}

	template<typename RAIter>
	void build(int x, int b, int e, const RAIter &a)
	{
		if (b+1 == e)
		{
			st[x].nod.build(a[b]);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);

		build(x + 1, b, m, a);
		build(y, m, e, a);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if_not(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (st[x].break_condition(lazy)) return;

		if (lo <= b && e <= hi && st[x].tag_condition(lazy))
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	node_container query(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
			return st[x].nod;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m >= hi) return query(x + 1, b, m);
		if (m <= lo) return query(y, m, e);
		return query(x + 1, b, m) + query(y, m, e);
	}

	template<class P>
	int find_first(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m && f(st[x + 1]))
		{
			auto t = find_first(x + 1, b, m, f);
			if (t != -1) return t;
		}
		if (m < hi && f(st[y]))
			return find_first(y, m, e, f);
		return -1;
	}

	template<class P>
	int find_last(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m < hi && f(st[y]))
		{
			auto t = find_last(y, m, e, f);
			if (t != -1) return t;
		}
		if (lo < m && f(st[x + 1]))
			return find_last(x + 1, b, m, f);
		return -1;
	}

	lazy_container lazy;
	int n, lo, hi;
	vector<node> st;

public:
	template<typename RAIter>
	void build(const RAIter &a)
	{
		build(0, 0, n, a);
	}

	void update(int l, int r, const lazy_container &x)
	{
		lo = l, hi = r, lazy = x, update_(0, 0, n);
	}

	node_container query(int l, int r)
	{
		return lo = l, hi = r, query(0, 0, n);
	}

	template<class P>
	int find_first(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_first(0, 0, n, f);
	}

	template<class P>
	int find_last(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_last(0, 0, n, f);
	}

	segment_tree(int n) : n(n), st(2 * n - 1) {}
};

struct node
{
	struct node_container
	{
		int x;

		template<typename T>
		inline void build(const T &a) // build(leave) from a
		{
			x = a;
		}

		friend node_container operator+(node_container l, const node_container &r) // merge l and r
		{
			l.x = max(l.x, r.x);
			return l;
		}

		node_container() : x(0) {}
	} nod;

	struct lazy_container
	{
		int add;

		inline bool operator()() // has lazy
		{
			return add != 0;
		}

		lazy_container(int add = 0) : add(add) {}
	} lazy;

	inline void apply(int l, int r, lazy_container &p) // apply lazy
	{
		nod.x += p.add;
		lazy.add += p.add;
	}
};

struct seg
{
	int l, r, c;
};

int main()
{
	#ifdef TurnRed
		//freopen("a.in", "r", stdin);
		//freopen("a.out", "w", stdout);
	#endif

	ios_base::sync_with_stdio(0), cin.tie(0);

	int n;
	cin >> n;
	vector<seg> a(n);
	vector<int> all;
	for (auto &i : a)
	{
		cin >> i.l >> i.r >> i.c;
		all.push_back(i.l);
		all.push_back(i.r);
	}

	auto get = [&](int x) { return lower_bound(all.begin(), all.end(), x) - all.begin() + 1; };
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	for (auto &i : a)
		i.l = get(i.l), i.r = get(i.r);

	const int sz = all.size() + 1;
	segment_tree<node, seg_lazy> st1(sz), st2(sz);
	vector<vector<int>> end(sz);
	for (auto &i : a)
		end[i.r].push_back(&i-&a[0]);

	for (int i = 1; i < sz; ++i)
	{
		for (auto j : end[i])
		{
			if (a[j].c == 1) st2.update(0, a[j].l, +1);
			else st1.update(0, a[j].l, +1);
		}

		int best = max(st1.query(0, i).x, st2.query(0, i).x);
		st1.update(i, i+1, best);
		st2.update(i, i+1, best);
	}

	cout << max(st1.query(0, sz).x, st2.query(0, sz).x) << "\n";

	return 0;
}


#include <bits/stdc++.h>

using namespace std;

#ifdef NeverBeRed
#include "debug.h"
#else
#define debug(...) 9715
#endif
typedef long long ll;
typedef long double ld;
typedef complex<ll> point;
#define F first
#define S second

enum seg_type { seg_not_lazy, seg_lazy, seg_beats };
template<class node, seg_type type>
struct segment_tree
{
	using node_container = typename node::node_container;
	using lazy_container = typename node::lazy_container;
	#define enable_if(x) template <const bool T = (x), typename enable_if<T, bool>::type = 0>
	#define enable_if_not(x) template <const bool T = (x), typename enable_if<!T, bool>::type = 0>

private:
	enable_if(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m)
	{
		if (st[x].lazy())
		{
			st[x + 1].apply(b, m, st[x].lazy);
			st[x + ((m - b) << 1)].apply(m, e, st[x].lazy);
			st[x].lazy = lazy_container();
		}
	}

	enable_if_not(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m) {}

	template<typename RAIter>
	void build(int x, int b, int e, const RAIter &a)
	{
		if (b+1 == e)
		{
			st[x].nod.build(a[b]);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);

		build(x + 1, b, m, a);
		build(y, m, e, a);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if_not(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (st[x].break_condition(lazy)) return;

		if (lo <= b && e <= hi && st[x].tag_condition(lazy))
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	node_container query(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
			return st[x].nod;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m >= hi) return query(x + 1, b, m);
		if (m <= lo) return query(y, m, e);
		return query(x + 1, b, m) + query(y, m, e);
	}

	template<class P>
	int find_first(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m && f(st[x + 1]))
		{
			auto t = find_first(x + 1, b, m, f);
			if (t != -1) return t;
		}
		if (m < hi && f(st[y]))
			return find_first(y, m, e, f);
		return -1;
	}

	template<class P>
	int find_last(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m < hi && f(st[y]))
		{
			auto t = find_last(y, m, e, f);
			if (t != -1) return t;
		}
		if (lo < m && f(st[x + 1]))
			return find_last(x + 1, b, m, f);
		return -1;
	}

	lazy_container lazy;
	int n, lo, hi;
	vector<node> st;

public:
	template<typename RAIter>
	void build(const RAIter &a)
	{
		build(0, 0, n, a);
	}

	void update(int l, int r, const lazy_container &x)
	{
		lo = l, hi = r, lazy = x, update_(0, 0, n);
	}

	node_container query(int l, int r)
	{
		return lo = l, hi = r, query(0, 0, n);
	}

	template<class P>
	int find_first(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_first(0, 0, n, f);
	}

	template<class P>
	int find_last(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_last(0, 0, n, f);
	}

	segment_tree(int n) : n(n), st(2 * n - 1) {}
};

struct node
{
	struct node_container
	{
		ll x;

		template<typename T>
		inline void build(const T &a) // build(leave) from a
		{
			x = a;
		}

		friend node_container operator+(node_container l, const node_container &r) // merge l and r
		{
			l.x = max(l.x, r.x);
			return l;
		}

		node_container() : x(0) {}
	} nod;

	struct lazy_container
	{
		ll add;

		inline bool operator()() // has lazy
		{
			return add != 0;
		}

		lazy_container(ll add = 0) : add(add) {}
	} lazy;

	inline void apply(int l, int r, lazy_container &p) // apply lazy
	{
		nod.x += p.add;
		lazy.add += p.add;
	}
};

struct seg
{
	int l, r, c;
};

int main()
{
	#ifdef TurnRed
		//freopen("a.in", "r", stdin);
		//freopen("a.out", "w", stdout);
	#endif

	ios_base::sync_with_stdio(0), cin.tie(0);

	int n;
	cin >> n;
	vector<seg> a(n);
	vector<int> all;
	for (auto &i : a)
	{
		cin >> i.l >> i.r >> i.c;
		for (int j = -1; j <= 1; ++j)
		{
			all.push_back(i.l + j);
			all.push_back(i.r + j);
		}
	}

	auto get = [&](int x) { return lower_bound(all.begin(), all.end(), x) - all.begin(); };
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	for (auto &i : a)
		i.l = get(i.l), i.r = get(i.r);

	const int sz = all.size();
	segment_tree<node, seg_lazy> st1(sz), st2(sz);
	vector<vector<int>> end(sz);
	for (auto &i : a)
		end[i.r].push_back(&i-&a[0]);

	for (int i = 1; i < sz; ++i)
	{
		for (auto j : end[i])
		{
			if (a[j].c == 1 && a[j].l > 0) st2.update(0, a[j].l, +1);
			if (a[j].c == 2 && a[j].l > 0) st1.update(0, a[j].l, +1);
		}

		int best = max(st1.query(0, i).x, st2.query(0, i).x);
		st1.update(i, i+1, best);
		st2.update(i, i+1, best);
	}

	cout << max(st1.query(0, sz).x, st2.query(0, sz).x) << "\n";

	return 0;
}


#include <bits/stdc++.h>

using namespace std;

#ifdef NeverBeRed
#include "debug.h"
#else
#define debug(...) 9715
#endif
typedef long long ll;
typedef long double ld;
typedef complex<ll> point;
#define F first
#define S second

enum seg_type { seg_not_lazy, seg_lazy, seg_beats };
template<class node, seg_type type>
struct segment_tree
{
	using node_container = typename node::node_container;
	using lazy_container = typename node::lazy_container;
	#define enable_if(x) template <const bool T = (x), typename enable_if<T, bool>::type = 0>
	#define enable_if_not(x) template <const bool T = (x), typename enable_if<!T, bool>::type = 0>

private:
	enable_if(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m)
	{
		if (st[x].lazy())
		{
			st[x + 1].apply(b, m, st[x].lazy);
			st[x + ((m - b) << 1)].apply(m, e, st[x].lazy);
			st[x].lazy = lazy_container();
		}
	}

	enable_if_not(type != seg_not_lazy)
	inline void push(int x, int b, int e, int m) {}

	template<typename RAIter>
	void build(int x, int b, int e, const RAIter &a)
	{
		if (b+1 == e)
		{
			st[x].nod.build(a[b]);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);

		build(x + 1, b, m, a);
		build(y, m, e, a);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if_not(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	enable_if(type == seg_beats)
	void update_(int x, int b, int e)
	{
		if (st[x].break_condition(lazy)) return;

		if (lo <= b && e <= hi && st[x].tag_condition(lazy))
		{
			st[x].apply(b, e, lazy);
			return;
		}

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m) update_(x + 1, b, m);
		if (m < hi) update_(y, m, e);
		st[x].nod = st[x + 1].nod + st[y].nod;
	}

	node_container query(int x, int b, int e)
	{
		if (lo <= b && e <= hi)
			return st[x].nod;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m >= hi) return query(x + 1, b, m);
		if (m <= lo) return query(y, m, e);
		return query(x + 1, b, m) + query(y, m, e);
	}

	template<class P>
	int find_first(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (lo < m && f(st[x + 1]))
		{
			auto t = find_first(x + 1, b, m, f);
			if (t != -1) return t;
		}
		if (m < hi && f(st[y]))
			return find_first(y, m, e, f);
		return -1;
	}

	template<class P>
	int find_last(int x, int b, int e, const P &f)
	{
		if (b+1 == e)
			return f(st[x]) ? b : -1;

		int m = (b + e) >> 1;
		int y = x + ((m - b) << 1);
		push(x, b, e, m);

		if (m < hi && f(st[y]))
		{
			auto t = find_last(y, m, e, f);
			if (t != -1) return t;
		}
		if (lo < m && f(st[x + 1]))
			return find_last(x + 1, b, m, f);
		return -1;
	}

	lazy_container lazy;
	int n, lo, hi;
	vector<node> st;

public:
	template<typename RAIter>
	void build(const RAIter &a)
	{
		build(0, 0, n, a);
	}

	void update(int l, int r, const lazy_container &x)
	{
		lo = l, hi = r, lazy = x, update_(0, 0, n);
	}

	node_container query(int l, int r)
	{
		return lo = l, hi = r, query(0, 0, n);
	}

	template<class P>
	int find_first(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_first(0, 0, n, f);
	}

	template<class P>
	int find_last(int l, int r, const P &f)
	{
		return lo = l, hi = r, find_last(0, 0, n, f);
	}

	segment_tree(int n) : n(n), st(2 * n - 1) {}
};

struct node
{
	struct node_container
	{
		ll x;

		template<typename T>
		inline void build(const T &a) // build(leave) from a
		{
			x = a;
		}

		friend node_container operator+(node_container l, const node_container &r) // merge l and r
		{
			l.x = max(l.x, r.x);
			return l;
		}

		node_container() : x(0) {}
	} nod;

	struct lazy_container
	{
		ll add;

		inline bool operator()() // has lazy
		{
			return add != 0;
		}

		lazy_container(ll add = 0) : add(add) {}
	} lazy;

	inline void apply(int l, int r, lazy_container &p) // apply lazy
	{
		nod.x += p.add;
		lazy.add += p.add;
	}
};

struct seg
{
	int l, r, c;
};

int main()
{
	#ifdef TurnRed
		//freopen("a.in", "r", stdin);
		//freopen("a.out", "w", stdout);
	#endif

	ios_base::sync_with_stdio(0), cin.tie(0);

	int n;
	cin >> n;
	vector<seg> a(n);
	vector<int> all;
	for (auto &i : a)
	{
		cin >> i.l >> i.r >> i.c;
		all.push_back(i.l);
		all.push_back(i.r);
	}

	auto get = [&](int x) { return lower_bound(all.begin(), all.end(), x) - all.begin() + 1; };
	sort(all.begin(), all.end());
	all.erase(unique(all.begin(), all.end()), all.end());
	for (auto &i : a)
		i.l = get(i.l), i.r = get(i.r);

	const int sz = all.size() + 1;
	segment_tree<node, seg_lazy> st1(sz), st2(sz);
	vector<vector<int>> end(sz);
	for (auto &i : a)
		end[i.r].push_back(&i-&a[0]);

	for (int i = 1; i < sz; ++i)
	{
		for (auto j : end[i])
		{
			if (a[j].c == 1 && a[j].l > 0) st2.update(0, a[j].l, +1);
			if (a[j].c == 2 && a[j].l > 0) st1.update(0, a[j].l, +1);
		}

		int best = max(st1.query(0, i).x, st2.query(0, i).x);
		st1.update(i, i+1, best);
		st2.update(i, i+1, best);
	}

	cout << max(st1.query(0, sz).x, st2.query(0, sz).x) << "\n";

	return 0;
}


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

#define f first
#define s second
#define pb push_back
#define mp make_pair
#define all(v) v.begin(), v.end()
#define sz(v) (int)v.size()

#define MOO(i, a, b) for(int i=a; i<b; i++)
#define M00(i, a) for(int i=0; i<a; i++)
#define MOOd(i,a,b) for(int i = (b)-1; i >= a; i--)
#define M00d(i,a) for(int i = (a)-1; i>=0; i--)

#define FAST ios::sync_with_stdio(0); cin.tie(0);
#define finish(x) return cout << x << '\n', 0;
#define dbg(x) cerr << ">>> " << #x << " = " << x << "\n";
#define _ << " _ " <<

template<class T> bool ckmin(T& a, const T& b) {return b < a ? a = b, 1 : 0;}
template<class T> bool ckmax(T& a, const T& b) {return a < b ? a = b, 1 : 0;}

typedef long long ll;
typedef long double ld;
typedef vector<int> vi;
typedef pair<int,int> pi;
typedef pair<ld,ld> pd;
typedef complex<ld> cd;

struct seg {
    int id, l, r;
    bool start, type;
    seg(int ID, int L, int R, bool START, bool TYPE) {
        id = ID; l = L; r = R; start = START; type = TYPE;
    }
};

bool operator<(const seg& s1, const seg& s2) {
    int idx1 = s1.r; if(s1.start) idx1 = s1.l;
    int idx2 = s2.r; if(s2.start) idx2 = s2.l;
    if(idx1 != idx2) return idx1 < idx2;
    return s1.start > s2.start;
}

const int MAX_N = 200020;
int n;

int main() { FAST
    int n; cin >> n;
    vector<seg> segs;
    M00(i, n) {
        int l, r, t; cin >> l >> r >> t;
        segs.pb(seg(i, l, r, 1, t==1));
        segs.pb(seg(i, l, r, 0, t==1));
    }
    sort(all(segs));
    int cnt = 0;
    set<pi> cur[2];
    M00(i, sz(segs)) {
        //dbg(segs[i].id _ segs[i].start _ segs[i].l _ segs[i].r _ segs[i].type);
        if(segs[i].start) {
            cur[segs[i].type].insert({segs[i].r, segs[i].id});
        } else {
            if(cur[segs[i].type].count({segs[i].r, segs[i].id})) {
                if(!cur[!segs[i].type].empty()) {
                    cnt++;
                    cur[!segs[i].type].erase(cur[!segs[i].type].begin());
                }
                cur[segs[i].type].erase({segs[i].r, segs[i].id});
            } else {
                continue;
            }
        }
    }
    cout << n-cnt << "\n";
}


#include<bits/stdc++.h>
#define o 1000005
#define re register int
#define ll long long
#define g0(a) memset(a,0,sizeof(a))
#define gc(a,b) memcpy(a,b,sizeof(a))
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define REP(i,a,b) for(int i=a;i>=b;i--)
#define pa pair<int,int>
#define mpp make_pair
#define s1 first
#define s2 second
using namespace std;
inline int read()
{
	register int data=0,w=1;
	char ch=0;
	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(ch>='0'&&ch<='9')data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data*w;
}
int n,L[o],R[o],t[o];
struct Seg
{
	int Max[o<<1],tag[o<<1];
	void add(int x,int v){tag[x]+=v,Max[x]+=v;}
	void pushup(int x){Max[x]=max(Max[x<<1],Max[x<<1|1]);}
	void pushdown(int x)
	{
		if(tag[x])
			add(x<<1,tag[x]),add(x<<1|1,tag[x]),
			tag[x]=0;
	}
	void update(int x,int l,int r,int ql,int qr,int v)
	{
		if(ql<=l&&r<=qr)return add(x,v),void();
		pushdown(x);
		int mid=l+r>>1;
		if(ql<=mid)update(x<<1,l,mid,ql,qr,v);
		if(mid<qr)update(x<<1|1,mid+1,r,ql,qr,v);
		pushup(x);
	}
}T[2];
int p[o];
vector<int> vc[400005];
int main()
{
	n=read();
	FOR(i,1,n)
		L[i]=read(),R[i]=read(),t[i]=read()-1,p[++p[0]]=L[i],p[++p[0]]=R[i];
	sort(p+1,p+p[0]+1);
	p[0]=unique(p+1,p+p[0]+1)-p-1;
	//cout<<p[0]<<'\n';
	FOR(i,1,n)L[i]=lower_bound(p+1,p+p[0]+1,L[i])-p,R[i]=lower_bound(p+1,p+p[0]+1,R[i])-p,vc[R[i]].push_back(i);
	FOR(i,1,p[0])
	{
		for(auto x:vc[i])T[t[x]].update(1,0,p[0],0,L[x]-1,1);
		int v=max(T[0].Max[1],T[1].Max[1]);
		//if(i==7)cout<<"v="<<v<<'\n';
		if(i==p[0])return cout<<v<<'\n',0;
		T[0].update(1,0,p[0],i,i,v);
		T[1].update(1,0,p[0],i,i,v);
	}
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5 + 10;
struct LINE{
	int l, r, t;
	LINE(){}
	LINE(int x, int y, int z){l = x, r = y, t = z;}
	bool operator < (const LINE & other)
	const{return r < other.r || (r == other.r && t < other.t) || (r == other.r && t == other.t && l > other.l);}
}line[N];
int tr[2][N * 8], tag[2][N * 8], h[N * 2], f[2][N * 2];
void pushdown(int opt, int x)
{
	if(tag[opt][x])
	{
		tr[opt][x * 2] += tag[opt][x];
		tr[opt][x * 2 + 1] += tag[opt][x];
		tag[opt][x * 2] += tag[opt][x];
		tag[opt][x * 2 + 1] += tag[opt][x];
		tag[opt][x] = 0;
	 }
}
void update(int opt, int x, int l, int r, int ll, int rr, int v)
{
	if(r < ll || l > rr)	return;
	if(ll <= l && r <= rr)
	{
		tr[opt][x] += v;
		tag[opt][x] +=v;
		return;
	}
	pushdown(opt, x);
	int mid = (l + r) >> 1;
	update(opt, x * 2, l, mid, ll, rr, v);
	update(opt, x * 2 + 1, mid + 1, r, ll, rr, v);
	tr[opt][x] = max(tr[opt][x * 2], tr[opt][x * 2 + 1]);
}
int main()
{
	int n;
	scanf("%d", &n);
	int cnt = 0;
	for(int i = 1; i <= n; i++)
	{
		int l, r, t;
		scanf("%d%d%d", &l, &r, &t);
		h[++cnt] = l;
		h[++cnt] = r;
		line[i] = LINE(l, r, t - 1);
	 }
	 h[++cnt] = 0;
	sort(line + 1, line + n + 1);
	sort(h + 1, h + cnt + 1);
	int m = unique(h + 1, h + cnt+ 1) - h - 1;
	/*for(int i = 1; i <= m; i++)
		printf("%d ",h[i]);
	cout<<endl;*/
	for(int i = 1; i <= n; i++)
	{
		line[i].l = lower_bound(h + 1, h + m + 1, line[i].l) - h;
		line[i].r = lower_bound(h + 1, h + m + 1, line[i].r) - h;
	}
	//cout<<"<<<<\n";
	//for(int i = 1;i <= n; i++)
	//	cout<<line[i].l<<" "<<line[i].r<<" "<<line[i].t<<endl;
	int ans = 0;
	int j = 1;
	for(int i = 1; i <= m; i++)
	{
		for(int opt = 0; opt <= 1; opt++)
		{
			while(j <= n && line[j].r == i && line[j].t == opt)
			{
				update(opt, 1, 1, m, 1, line[j].l - 1, 1);
				j++;
			}
			f[opt][i] = tr[opt][1];
			ans = max(ans, f[opt][i]);

		}
		update(1, 1, 1, m, i, i, f[0][i]);
		update(0, 1, 1, m, i, i, f[1][i]);
		/*cout<<"tree:\n";
		for(int k = 1; k <= 10; k ++)
			cout<<tr[0][k]<<endl;
		for(int k = 1; k <= 10; k++)
			cout<<tr[1][k]<<endl;*/
	}
	/*for(int opt = 0; opt <= 1; opt++)
	{
		for(int i = 1; i <= m; i++)
			cout<<f[opt][i]<<" ";
		cout<<endl;
	}*/
	cout<<ans<<endl;
	return 0;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5 + 10;
struct LINE{
	int l, r, t;
	LINE(){}
	LINE(int x, int y, int z){l = x, r = y, t = z;}
	bool operator < (const LINE & other)
	const{return r < other.r || (r == other.r && t < other.t) || (r == other.r && t == other.t && l > other.l);}
}line[N];
int tr[2][N * 8], h[N * 2], f[2][N * 2];
int query_max(int opt, int x, int l, int r, int pos)
{
	if(pos < l)	return 0;
	if(pos >= r)	return tr[opt][x];
	int mid = (l + r) >> 1;
	return max(query_max(opt, x * 2, l, mid, pos) , query_max(opt, x * 2 + 1, mid + 1, r, pos));
}
void update(int opt, int x, int l, int r, int pos, int v)
{
	if(r < pos || l > pos)	return;
	if(l == r)
	{
		tr[opt][x] = v;
		return;
	}
	int mid = (l + r) >> 1;
	update(opt, x * 2, l, mid, pos, v);
	update(opt, x * 2 + 1, mid + 1, r, pos, v);
	tr[opt][x] = max(tr[opt][x * 2], tr[opt][x * 2 + 1]);
}
int main()
{
	int n;
	scanf("%d", &n);
	int cnt = 0;
	for(int i = 1; i <= n; i++)
	{
		int l, r, t;
		scanf("%d%d%d", &l, &r, &t);
		h[++cnt] = l;
		h[++cnt] = r;
		line[i] = LINE(l, r, t - 1);
	 }
	sort(line + 1, line + n + 1);
	sort(h + 1, h + cnt + 1);
	int m = unique(h + 1, h + cnt+ 1) - h - 1;
	/*for(int i = 1; i <= m; i++)
		printf("%d ",h[i]);
	cout<<endl;*/
	for(int i = 1; i <= n; i++)
	{
		line[i].l = lower_bound(h + 1, h + m + 1, line[i].l) - h;
		line[i].r = lower_bound(h + 1, h + m + 1, line[i].r) - h;
	}
	//for(int i = 1;i <= n; i++)
	//	cout<<line[i].l<<" "<<line[i].r<<" "<<line[i].t<<endl;
	int ans = 0;
	int j = 1;
	for(int i = 1; i <= m; i++)
	{
		for(int opt = 0; opt <= 1; opt++)
		{
			f[opt][i] = f[opt][i - 1];
			int cnt = 0;
			while(j <= n && line[j].r == i && line[j].t == opt)
			{
				int now = query_max(opt ^ 1, 1, 1, m, line[j].l - 1);
				//cout<<now<<endl;
				cnt++;
				f[opt][i] = max(f[opt][i], now + cnt);
				j++;
			}
			f[opt][i] = max(f[opt][i], f[opt][i - 1] + cnt);
			ans = max(ans, f[opt][i]);
			update(opt, 1, 1, m, i, f[opt][i]);
		}
		/*cout<<"tree:\n";
		for(int k = 1; k <= 10; k ++)
			cout<<tr[0][k]<<endl;
		for(int k = 1; k <= 10; k++)
			cout<<tr[1][k]<<endl;*/
	}
	/*for(int opt = 0; opt <= 1; opt++)
	{
		for(int i = 1; i <= m; i++)
			cout<<f[opt][i]<<" ";
		cout<<endl;
	}*/
	cout<<ans<<endl;
	return 0;
}


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

inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
	return x * f;
}

const int N = 2e5 + 5;
int l[N], r[N], t[N];
vector<pair<int, pair<int, int> > > arr;
set<pair<int, int> > s[2];

int main()
{
	int n = read();
	for (int i = 1; i <= n; ++i)
	{
		l[i] = read();
		r[i] = read();
		t[i] = read() - 1;

		arr.push_back({l[i], {0, i}});
		arr.push_back({r[i], {1, i}});
	}

	sort(arr.begin(), arr.end());

	int ans = 0;
	for (auto it : arr)
	{
		int i = it.second.second;
		int opt = it.second.first;

		if (opt == 0) s[t[i]].insert({r[i], i});
		if (opt == 1)
		{
			if (s[t[i]].count({r[i], i}) && !s[!t[i]].empty())
			{
				++ans;
				s[!t[i]].erase(s[!t[i]].begin());
			}
			if (s[t[i]].count({r[i], i}))
				s[t[i]].erase({r[i], i});
		}
	}

	printf("%d\n", n - ans);

	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define f first
#define s second
set<pair<int, int>>se[2];
vector<pair<int, pair<int, int>>>v;
int main() {
	int n;
	cin>>n;
	int l[n], r[n], t[n];
	for(int i=0; i<n; i++){
		cin>>l[i]>>r[i]>>t[i];
		t[i]--;
		v.pb({l[i], {0, i}});
		v.pb({r[i], {1, i}});
	}
	sort(v.begin(), v.end());
	int ans=0;
	for(auto u:v){
		int ind=u.s.s;
		int ty=t[u.s.s];
		if(u.s.f){
			if(se[ty].count({r[ind], ind}) && !se[1-ty].empty()){
				se[1-ty].erase(se[1-ty].begin());
				ans++;
			}
			if(se[ty].count({r[ind], ind})){
				se[ty].erase({r[ind], ind});
			}
		}
		else{
			se[ty].insert({r[ind], ind});
		}
	}
	cout<<n-ans<<endl;
	return 0;
}


#include <bits/stdc++.h>
#define ld long double
#define endl "\n"
#define fastio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb(x) push_back(x)
#define mp(a,b) make_pair(a,b)
#define ms(v,x) memset(v,x,sizeof(v))
#define all(v) v.begin(),v.end()
#define ff first
#define ss second
#define rep(i, a, b) for(int i = a; i < (b); ++i)
#define per(i, a, b) for(int i = b-1; i>=a ; i--)
#define trav(a, x) for(auto& a : x)
#define allin(a , x) for(auto a : x)
#define td(v) v.begin(),v.end()
//#define sz(v) (int)v.size()
//#define int long long
using namespace std;
typedef vector<int> vi;
#define y1 abacaba
#define left sefude
#define db(x) cerr << #x <<" == "<<x << endl;
#define db2(x,y) cerr<<#x <<" == "<<x<<", "<<#y<<" == "<<y<<endl;
typedef long long ll;
typedef pair<int,int> pii;
inline ll mod(ll n, ll m ){ ll ret = n%m; if(ret < 0) ret += m; return ret; }
ll gcd(ll a, ll b){return (b == 0LL ? a : gcd(b, a%b));}
ll exp(ll a,ll b,ll m){
    if(b==0LL) return 1LL;
    if(b==1LL) return mod(a,m);
    ll k = mod(exp(a,b/2,m),m);
    if(b&1LL){
        return mod(a*mod(k*k,m),m);
    }
    else return mod(k*k,m);
}

const int N = 2e5 + 100;
int l[N],r[N],t[N];
multiset<pii> S[2];
int32_t main(){
  fastio;
  int n;
  cin>>n;
  int matching = 0;

  vector<pii> v;
  for(int i=0;i<n;i++){
    cin >> l[i] >> r[i] >> t[i];
    t[i] -- ;
    v.pb(pii(i,0));
    v.pb(pii(i,1));
  }
  sort(all(v),[&](pii a,pii b){
    int A = (a.ss == 0 ? l[a.ff] : r[a.ff]);
    int B = (b.ss == 0 ? l[b.ff] : r[b.ff]);
    if(A == B)return a.ss < b.ss;
    return A < B;
  });

  for(auto it : v){
    int id = it.ff;
    int tt = t[id];
    if(it.ss == 0){
      // add
      S[tt].insert(pii(r[id],l[id]));
    }else{
      if(S[tt].find(pii(r[id],l[id]))!=S[tt].end()){
        S[tt].erase(S[tt].find(pii(r[id],l[id])));
      }
      if(S[tt^1].size()){
        auto A = *S[tt^1].begin();
        S[tt^1].erase(S[tt^1].find(A));
        matching++;
      }
    }
  }

  cout << n - matching << endl;

  // math -> gcd it all
  // Did u check N=1? Did you switch N,M?
}


#include <bits/stdc++.h>
#define ld long double
#define endl "\n"
#define fastio ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define pb(x) push_back(x)
#define mp(a,b) make_pair(a,b)
#define ms(v,x) memset(v,x,sizeof(v))
#define all(v) v.begin(),v.end()
#define ff first
#define ss second
#define rep(i, a, b) for(int i = a; i < (b); ++i)
#define per(i, a, b) for(int i = b-1; i>=a ; i--)
#define trav(a, x) for(auto& a : x)
#define allin(a , x) for(auto a : x)
#define td(v) v.begin(),v.end()
//#define sz(v) (int)v.size()
//#define int long long
using namespace std;
typedef vector<int> vi;
#define y1 abacaba
#define left sefude
#define db(x) cerr << #x <<" == "<<x << endl;
#define db2(x,y) cerr<<#x <<" == "<<x<<", "<<#y<<" == "<<y<<endl;
typedef long long ll;
typedef pair<int,int> pii;
inline ll mod(ll n, ll m ){ ll ret = n%m; if(ret < 0) ret += m; return ret; }
ll gcd(ll a, ll b){return (b == 0LL ? a : gcd(b, a%b));}
ll exp(ll a,ll b,ll m){
    if(b==0LL) return 1LL;
    if(b==1LL) return mod(a,m);
    ll k = mod(exp(a,b/2,m),m);
    if(b&1LL){
        return mod(a*mod(k*k,m),m);
    }
    else return mod(k*k,m);
}

const int N = 2e5 + 100;
int l[N],r[N],t[N];
multiset<pii> S[2];
int32_t main(){
  fastio;
  int n;
  cin>>n;
  int matching = 0;

  vector<pii> v;
  for(int i=0;i<n;i++){
    cin >> l[i] >> r[i] >> t[i];
    t[i] -- ;
    v.pb(pii(i,0));
    v.pb(pii(i,1));
  }
  sort(all(v),[&](pii a,pii b){
    int A = (a.ss == 0 ? l[a.ff] : r[a.ff]);
    int B = (b.ss == 0 ? l[b.ff] : r[b.ff]);
    if(A == B)return a.ss < b.ss;
    return A < B;
  });

  for(auto it : v){
    int id = it.ff;
    int tt = t[id];
    if(it.ss == 0){
      // add
      S[tt].insert(pii(r[id],l[id]));
    }else{
      if(S[tt].find(pii(r[id],l[id]))!=S[tt].end()){
        S[tt].erase(S[tt].find(pii(r[id],l[id])));

        if(S[tt^1].size()){
          auto A = *S[tt^1].begin();
          S[tt^1].erase(S[tt^1].find(A));
          matching++;
        }
      }
    }
  }

  cout << n - matching << endl;

  // math -> gcd it all
  // Did u check N=1? Did you switch N,M?
}


#include <bits/stdc++.h>
#define fst first
#define snd second
#define fore(i,a,b) for(int i=a,ThxDem=b;i<ThxDem;++i)
#define pb push_back
#define ALL(s) s.begin(),s.end()
#define FIN ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define SZ(s) int(s.size())
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;

struct STree { // example: range sum with range addition
	vector<int> st,lazy;int n;
	STree(int n): st(4*n+5,0), lazy(4*n+5,0), n(n) {}
	void push(int k, int s, int e){
		if(!lazy[k])return; // if neutral, nothing to do
		st[k]+=lazy[k]; // update st according to lazy
		if(s+1<e){ // propagate to children
			lazy[2*k]+=lazy[k];
			lazy[2*k+1]+=lazy[k];
		}
		lazy[k]=0; // clear node lazy
	}
	void upd(int k, int s, int e, int a, int b, int v){
		push(k,s,e);
		if(s>=b||e<=a)return;
		if(s>=a&&e<=b){
			lazy[k]+=v; // accumulate lazy
			push(k,s,e);return;
		}
		int m=(s+e)/2;
		upd(2*k,s,m,a,b,v);upd(2*k+1,m,e,a,b,v);
		st[k]=max(st[2*k],st[2*k+1]); // operation
	}
	int query(int k, int s, int e, int a, int b){
		if(s>=b||e<=a)return 0; // operation neutral
		push(k,s,e);
		if(s>=a&&e<=b)return st[k];
		int m=(s+e)/2;
		return max(query(2*k,s,m,a,b),query(2*k+1,m,e,a,b)); // operation
	}
	void upd(int a, int b, int v){upd(1,0,n,a,b,v);}
	int query(int a, int b){return query(1,0,n,a,b);}
}; // usage: STree rmq(n);rmq.init(x);rmq.upd(s,e,v);rmq.query(s,e);

vector<int> vs;

int get(int x){
	return lower_bound(ALL(vs),x)-vs.begin();
}

int main(){FIN;
	int n; cin>>n;
	vector<pair<ii,int>> v(n);
	fore(i,0,n){
		int x,y,t; cin>>x>>y>>t; vs.pb(x); vs.pb(y);
		v[i]={{x,y},t};
	}
	sort(ALL(vs));
	vs.erase(unique(ALL(vs)),vs.end());
	fore(i,0,n){
		v[i].fst.fst=get(v[i].fst.fst);
		v[i].fst.snd=get(v[i].fst.snd);
	}
	sort(ALL(v));
	int k=SZ(vs);
	STree st0(k+1),st1(k+1);
	for(int i=n-1;i>=0;i--){
		if(v[i].snd==1){
			int now=st1.query(v[i].fst.snd+1,k+1)+1;

			int pre=st0.query(v[i].fst.fst,v[i].fst.fst+1);
			st0.upd(v[i].fst.fst,v[i].fst.fst+1,max(now-pre,0));
			st1.upd(v[i].fst.snd+1,k+1,1);
		}
		else{
			int now=st0.query(v[i].fst.snd+1,k+1)+1;

			int pre=st1.query(v[i].fst.fst,v[i].fst.fst+1);
			st1.upd(v[i].fst.fst,v[i].fst.fst+1,max(now-pre,0));
			st0.upd(v[i].fst.snd+1,k+1,1);
		}
	}
	int ans=max(st0.query(0,k),st1.query(0,k));
	cout<<ans<<"\n";
}


#include <bits/stdc++.h>
#define fst first
#define snd second
#define fore(i,a,b) for(int i=a,ThxDem=b;i<ThxDem;++i)
#define pb push_back
#define ALL(s) s.begin(),s.end()
#define FIN ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define SZ(s) int(s.size())
using namespace std;
typedef long long ll;
typedef pair<int,int> ii;

struct STree { // example: range sum with range addition
	vector<int> st,lazy;int n;
	STree(int n): st(4*n+5,0), lazy(4*n+5,0), n(n) {}
	void push(int k, int s, int e){
		if(!lazy[k])return; // if neutral, nothing to do
		st[k]+=lazy[k]; // update st according to lazy
		if(s+1<e){ // propagate to children
			lazy[2*k]+=lazy[k];
			lazy[2*k+1]+=lazy[k];
		}
		lazy[k]=0; // clear node lazy
	}
	void upd(int k, int s, int e, int a, int b, int v){
		push(k,s,e);
		if(s>=b||e<=a)return;
		if(s>=a&&e<=b){
			lazy[k]+=v; // accumulate lazy
			push(k,s,e);return;
		}
		int m=(s+e)/2;
		upd(2*k,s,m,a,b,v);upd(2*k+1,m,e,a,b,v);
		st[k]=max(st[2*k],st[2*k+1]); // operation
	}
	int query(int k, int s, int e, int a, int b){
		if(s>=b||e<=a)return 0; // operation neutral
		push(k,s,e);
		if(s>=a&&e<=b)return st[k];
		int m=(s+e)/2;
		return max(query(2*k,s,m,a,b),query(2*k+1,m,e,a,b)); // operation
	}
	void upd(int a, int b, int v){upd(1,0,n,a,b,v);}
	int query(int a, int b){return query(1,0,n,a,b);}
}; // usage: STree rmq(n);rmq.init(x);rmq.upd(s,e,v);rmq.query(s,e);

vector<int> vs;

int get(int x){
	return lower_bound(ALL(vs),x)-vs.begin();
}

int main(){FIN;
	int n; cin>>n;
	vector<pair<ii,int>> v(n);
	fore(i,0,n){
		int x,y,t; cin>>x>>y>>t; vs.pb(x); vs.pb(y);
		v[i]={{x,y},t};
	}
	sort(ALL(vs));
	vs.erase(unique(ALL(vs)),vs.end());
	fore(i,0,n){
		v[i].fst.fst=get(v[i].fst.fst);
		v[i].fst.snd=get(v[i].fst.snd);
	}
	sort(ALL(v));
	int k=SZ(vs);
	STree st0(k),st1(k);
	for(int i=n-1;i>=0;i--){
		if(v[i].snd==1){
			int now=st1.query(v[i].fst.snd+1,k)+1;

			int pre=st0.query(v[i].fst.fst,v[i].fst.fst+1);
			st0.upd(v[i].fst.fst,v[i].fst.fst+1,max(now-pre,0));
			st1.upd(v[i].fst.snd+1,k,1);
		}
		else{
			int now=st0.query(v[i].fst.snd+1,k)+1;

			int pre=st1.query(v[i].fst.fst,v[i].fst.fst+1);
			st1.upd(v[i].fst.fst,v[i].fst.fst+1,max(now-pre,0));
			st0.upd(v[i].fst.snd+1,k,1);
		}
	}
	int ans=max(st0.query(0,k),st1.query(0,k));
	cout<<ans<<"\n";
}


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

#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define lch (o << 1)
#define rch (o << 1 | 1)

typedef double db;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int, int> pint;
typedef tuple<int, int, int> tint;

const int N = 4e5 + 5;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;

struct SegTree {
	int mx[N * 4], lazy[N * 4];

	void Tag(int o, int tag) {
		mx[o] += tag;
		lazy[o] += tag;
	}
	void Pushdown(int o) {
		if (!lazy[o]) return;
		Tag(lch, lazy[o]);
		Tag(rch, lazy[o]);
		lazy[o] = 0;
	}
	void Maintain(int o) {
		mx[o] = max(mx[lch], mx[rch]);
	}
	void Add(int o, int L, int R, int qL, int qR, int det) {
		if (qL > qR) return;
		if (qL <= L && R <= qR) {
			Tag(o, det);
			return;
		}
		Pushdown(o);
		int M = (L + R) / 2;
		if (qL <= M) Add(lch, L, M, qL, qR, det);
		if (M + 1 <= qR) Add(rch, M + 1, R, qL, qR, det);
		Maintain(o);
	}
	int Query(int o, int L, int R, int qL, int qR) {
		if (qL > qR) return 0;
		if (qL <= L && R <= qR) return mx[o];
		Pushdown(o);
		int M = (L + R) / 2;
		int ret = 0;
		if (qL <= M) ret = max(ret, Query(lch, L, M, qL, qR));
		if (M + 1 <= qR) ret = max(ret, Query(rch, M + 1, R, qL, qR));
		return ret;
	}
};
SegTree seg[2];

int main() {
	ios::sync_with_stdio(0);

	vector<int> oo = {0};

	int n;
	cin >> n;
	vector<tint> segs;
	for (int i = 1; i <= n; i++) {
		int l, r, type;
		cin >> l >> r >> type;
		oo.push_back(l);
		oo.push_back(r);
		segs.push_back(make_tuple(l, r, type - 1));
	}
	sort(all(oo));
	oo.erase(unique(all(oo)), oo.end());

	for (auto &s: segs) {
		get<0>(s) = lower_bound(all(oo), get<0>(s)) - oo.begin();
		get<1>(s) = lower_bound(all(oo), get<1>(s)) - oo.begin();
	}
	sort(all(segs), [](tint a, tint b) {
		if (get<1>(a) != get<1>(b))
			return get<1>(a) < get<1>(b);
		else
			return get<0>(a) < get<0>(b);
	});

	int ans = 0;
	int upp = oo.size() - 1;
	for (auto s: segs) {
		int l = get<0>(s);
		int r = get<1>(s);
		int type = get<2>(s);

		seg[type ^ 1].Add(1, 0, upp, 0, l - 1, 1);
		int f = seg[type ^ 1].Query(1, 0, upp, 0, l - 1);
		ans = max(ans, f);
		seg[type].Add(1, 0, upp, r, r, f);
		/*
		int g = seg[type].Query(1, 0, upp, r, r);
		if (f > g)
			seg[type].Add(1, 0, upp, r, r, f - g);
		*/
	}
	cout << ans << endl;

	return 0;
}


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

#define fi first
#define se second
#define all(x) x.begin(), x.end()
#define lch (o << 1)
#define rch (o << 1 | 1)

typedef double db;
typedef long long ll;
typedef unsigned int ui;
typedef pair<int, int> pint;
typedef tuple<int, int, int> tint;

const int N = 4e5 + 5;
const int INF = 0x3f3f3f3f;
const ll INF_LL = 0x3f3f3f3f3f3f3f3f;

struct SegTree {
	int mx[N * 4], lazy[N * 4];

	void Tag(int o, int tag) {
		mx[o] += tag;
		lazy[o] += tag;
	}
	void Pushdown(int o) {
		if (!lazy[o]) return;
		Tag(lch, lazy[o]);
		Tag(rch, lazy[o]);
		lazy[o] = 0;
	}
	void Maintain(int o) {
		mx[o] = max(mx[lch], mx[rch]);
	}
	void Add(int o, int L, int R, int qL, int qR, int det) {
		if (qL > qR) return;
		if (qL <= L && R <= qR) {
			Tag(o, det);
			return;
		}
		Pushdown(o);
		int M = (L + R) / 2;
		if (qL <= M) Add(lch, L, M, qL, qR, det);
		if (M + 1 <= qR) Add(rch, M + 1, R, qL, qR, det);
		Maintain(o);
	}
	int Query(int o, int L, int R, int qL, int qR) {
		if (qL > qR) return 0;
		if (qL <= L && R <= qR) return mx[o];
		Pushdown(o);
		int M = (L + R) / 2;
		int ret = 0;
		if (qL <= M) ret = max(ret, Query(lch, L, M, qL, qR));
		if (M + 1 <= qR) ret = max(ret, Query(rch, M + 1, R, qL, qR));
		return ret;
	}
};
SegTree seg[2];

int main() {
	ios::sync_with_stdio(0);

	vector<int> oo = {0};

	int n;
	cin >> n;
	vector<tint> segs;
	for (int i = 1; i <= n; i++) {
		int l, r, type;
		cin >> l >> r >> type;
		oo.push_back(l);
		oo.push_back(r);
		segs.push_back(make_tuple(l, r, type - 1));
	}
	sort(all(oo));
	oo.erase(unique(all(oo)), oo.end());

	for (auto &s: segs) {
		get<0>(s) = lower_bound(all(oo), get<0>(s)) - oo.begin();
		get<1>(s) = lower_bound(all(oo), get<1>(s)) - oo.begin();
	}
	sort(all(segs), [](tint a, tint b) {
		if (get<1>(a) != get<1>(b))
			return get<1>(a) < get<1>(b);
		else
			return get<0>(a) < get<0>(b);
	});

	int ans = 0;
	int upp = oo.size() - 1;
	for (auto s: segs) {
		int l = get<0>(s);
		int r = get<1>(s);
		int type = get<2>(s);

		seg[type ^ 1].Add(1, 0, upp, 0, l - 1, 1);
		int f = seg[type ^ 1].Query(1, 0, upp, 0, l - 1);
		ans = max(ans, f);
		int g = seg[type].Query(1, 0, upp, r, r);
		if (f > g)
			seg[type].Add(1, 0, upp, r, r, f - g);
	}
	cout << ans << endl;

	return 0;
}


#include <algorithm>
#include <iostream>
#include <set>
#include <vector>

constexpr int maxn = 200000;
struct Seg {
  int L;
  int R;
  int t;
};

bool operator < (Seg s1, Seg s2) {
  return s1.R < s2.R;
}

int main() {
  using namespace std;
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  unsigned n;
  cin >> n;
  vector<Seg> segs { n };
  for (Seg& s : segs) {
    cin >> s.L >> s.R >> s.t;
    s.t = s.t & 1; // normalize to 0-1 for convenience
  }

  sort(segs.begin(), segs.end());
  multiset<int> unpairedSegEnds[2];
  int matches = 0;
  for (const Seg& s : segs) {
    // This greedy matching strategy is optimal.
    multiset<int>& same = unpairedSegEnds[s.t];
    multiset<int>& opp = unpairedSegEnds[s.t ^ 1];
    multiset<int>::iterator tar = opp.lower_bound(s.L);
    if (tar == opp.end()) // if unable to pair s
      same.insert(s.R);
    else {
      opp.erase(tar);
      ++matches;
    }
  }

  // maxflow-mincut: best cut set has size (matches)
  // A vertex set is independent iff its complement is a cut set,
  // so max independent set has size
  cout << (n - matches) << '\n';

  return 0;
}


#include<cstdio>
#include<algorithm>
#include<vector>
struct Segment{
    int l,r;
    Segment(){}
    Segment(int l,int r):l(l),r(r){}
};
Segment segW[200005], segB[200005];
std::vector<int>leftW[400005], leftB[400005];
int sumW[400005], sumB[400005];
int map[400005];
int fW[400005], fB[400005];

int getId(int u,int n){
    int L = 1, R = n;
    int res = -1;
    while(L<=R){
        int M = (L+R)/2;
        if(map[M]<=u){
            res = M;
            L = M+1;
        }
        else R = M-1;
    }
    return res;
}

int preWork(int n,int sw,int sb){
    std::sort(map+1,map+1+n);
    int p = 1, size = 0;
    while(p<=n){
        int np = p;
        while(np+1<=n && map[np+1]==map[p]) np++;
        map[++size] = map[p];
        p = np+1;
    }
    for(int i = 1; i <= sw; i++){
        segW[i].l = getId(segW[i].l,size);
        segW[i].r = getId(segW[i].r,size);
        leftW[segW[i].r].push_back(segW[i].l);
    }
    for(int i = 1; i <= sb; i++){
        segB[i].l = getId(segB[i].l,size);
        segB[i].r = getId(segB[i].r,size);
        leftB[segB[i].r].push_back(segB[i].l);
    }
    int upper = size;
    sumW[0] = sumB[0] = 0;
    for(int i = 1; i <= upper; i++){
        sumW[i] = sumW[i-1]+(int)leftW[i].size();
        sumB[i] = sumB[i-1]+(int)leftB[i].size();
    }
    return size;
}

int max(int a,int b){ return a>b?a:b; }

struct SegmentT{
    int l,r;
    int c[2];
    int d[2];
    SegmentT(){
        c[0] = c[1] = 0;
        d[0] = d[1] = -100000007;
    }
};
SegmentT t[1600005];

SegmentT merge(SegmentT& u,SegmentT& v){
    SegmentT res;
    res.l = u.l, res.r = v.r;
    for(int i = 0; i < 2; i++) res.d[i] = max(u.d[i],v.d[i]);
    return res;
}

void build(int p,int l,int r){
    t[p].l = l, t[p].r = r;
    t[p].c[0] = t[p].c[1] = 0;
    if(l==r){
        //return global is ok?
        for(int i = 0; i < 2; i++) t[p].d[i] = -100000007;
    }
    else{
        int m = (l+r)/2;
        build(2*p,l,m);
        build(2*p+1,m+1,r);
        t[p] = merge(t[2*p],t[2*p+1]);
    }
}

void spread(int p){
    for(int i = 0; i < 2; i++){
        if(t[p].c[i]==0) continue;
        int c = t[p].c[i]; t[p].c[i] = 0;
        t[2*p].c[i] += c, t[2*p].d[i] += c;
        t[2*p+1].c[i] += c, t[2*p+1].d[i] += c;
    }
}

void change(int p,int l,int r,int v,int tag){
    if(l<=t[p].l && r>=t[p].r){
        t[p].c[tag] += v;
        t[p].d[tag] += v;
    }
    else{
        spread(p);
        int m = (t[p].l+t[p].r)/2;
        if(l<=m) change(2*p,l,r,v,tag);
        if(r>=m+1) change(2*p+1,l,r,v,tag);
        t[p] = merge(t[2*p],t[2*p+1]);
    }
}

void setV(int p,int x,int v,int tag){
    if(t[p].l==t[p].r){
        t[p].d[tag] = v;
    }
    else{
        spread(p);
        int m = (t[p].l+t[p].r)/2;
        if(x<=m) setV(2*p,x,v,tag);
        else setV(2*p+1,x,v,tag);
        t[p] = merge(t[2*p],t[2*p+1]);
    }
}

SegmentT ask(int p,int l,int r){
    if(l<=t[p].l && r>=t[p].r) return t[p];
    else{
        spread(p);
        SegmentT u,v;
        int m = (t[p].l+t[p].r)/2;
        if(l<=m) u = ask(2*p,l,r);
        if(r>=m+1) v = ask(2*p+1,l,r);
        return merge(u,v);
    }
}

void doMinus(int l,int r,int tag){
    if(l<=r) change(1,l,r,-1,tag);
    /*if(!tag){
        for(int i = l; i <= r; i++) fW[i]--;
    }
    else{
        for(int i = l; i <= r; i++) fB[i]--;
    }*/
}

int getMaxn(int r,int tag){
    int res = 0;
    if(r>=1) res = max(res,t[1].d[tag]);
    /*for(int i = 0; i <= r; i++){
        int cur = -1;
        if(!tag) cur = fW[i];
        else cur = fB[i];
        if(cur>res) res = cur;
    }*/
    return res;
}

void setValue(int p,int v,int tag){
    /*if(!tag) fW[p] = v;
    else fB[p] = v;*/
    setV(1,p,v,tag);
}

int main(){
    int n;
    scanf("%d",&n);
    int sizeW = 0, sizeB = 0;
    int size = 0;
    for(int i = 1; i <= n; i++){
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        if(c==1) segW[++sizeW] = Segment(l,r);
        else segB[++sizeB] = Segment(l,r);
        map[++size] = l;
        map[++size] = r;
    }
    int ans = 0;
    int upper = preWork(size,sizeW,sizeB);
    fW[0] = fB[0] = 0;
    build(1,1,upper);
    for(int i = 1; i <= upper; i++){
        for(int left: leftW[i]) doMinus(left,i-1,0);
        for(int left: leftB[i]) doMinus(left,i-1,1);
        int white = getMaxn(i-1,0)+sumW[i], black = getMaxn(i-1,1)+sumB[i];
        long long value = max(white,black);
        if(value>ans) ans = value;
        setValue(i,value-sumW[i],0);
        setValue(i,value-sumB[i],1);
    }
    printf("%d\n",ans);
    return 0;
}


#include<cstdio>
#include<algorithm>
#include<vector>
struct Segment{
    int l,r;
    Segment(){}
    Segment(int l,int r):l(l),r(r){}
};
Segment segW[200005], segB[200005];
std::vector<int>leftW[400005], leftB[400005];
int sumW[400005], sumB[400005];
int t[400005];
int fW[400005], fB[400005];

int getId(int u,int n){
    int L = 1, R = n;
    int res = -1;
    while(L<=R){
        int M = (L+R)/2;
        if(t[M]<=u){
            res = M;
            L = M+1;
        }
        else R = M-1;
    }
    return res;
}

int preWork(int n,int sw,int sb){
    std::sort(t+1,t+1+n);
    int p = 1, size = 0;
    while(p<=n){
        int np = p;
        while(np+1<=n && t[np+1]==t[p]) np++;
        t[++size] = t[p];
        p = np+1;
    }
    for(int i = 1; i <= sw; i++){
        segW[i].l = getId(segW[i].l,size);
        segW[i].r = getId(segW[i].r,size);
        leftW[segW[i].r].push_back(segW[i].l);
    }
    for(int i = 1; i <= sb; i++){
        segB[i].l = getId(segB[i].l,size);
        segB[i].r = getId(segB[i].r,size);
        leftB[segB[i].r].push_back(segB[i].l);
    }
    int upper = size;
    sumW[0] = sumB[0] = 0;
    for(int i = 1; i <= upper; i++){
        sumW[i] = sumW[i-1]+(int)leftW[i].size();
        sumB[i] = sumB[i-1]+(int)leftB[i].size();
    }
    return size;
}

void doMinus(int l,int r,int tag){
    if(!tag){
        for(int i = l; i <= r; i++) fW[i]--;
    }
    else{
        for(int i = l; i <= r; i++) fB[i]--;
    }
}

int getMaxn(int r,int tag){
    int res = -1;
    for(int i = 0; i <= r; i++){
        int cur = -1;
        if(!tag) cur = fW[i];
        else cur = fB[i];
        if(cur>res) res = cur;
    }
    return res;
}

int max(int a,int b){ return a>b?a:b; }

void setValue(int p,int v,int tag){
    if(!tag) fW[p] = v;
    else fB[p] = v;
}

int main(){
    int n;
    scanf("%d",&n);
    int sizeW = 0, sizeB = 0;
    int size = 0;
    for(int i = 1; i <= n; i++){
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        if(c==1) segW[++sizeW] = Segment(l,r);
        else segB[++sizeB] = Segment(l,r);
        t[++size] = l;
        t[++size] = r;
    }
    int ans = 0;
    int upper = preWork(size,sizeW,sizeB);
    fW[0] = fB[0] = 0;
    for(int i = 1; i <= upper; i++){
        for(int left: leftW[i]) doMinus(left,i-1,0);
        for(int left: leftB[i]) doMinus(left,i-1,1);
        int white = getMaxn(i-1,0)+sumW[i], black = getMaxn(i-1,1)+sumB[i];
        long long value = max(white,black);
        if(value>ans) ans = value;
        setValue(i,value-sumW[i],0);
        setValue(i,value-sumB[i],1);
    }
    printf("%d\n",ans);
    return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[2];
int32_t main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    #ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	#endif
	int n;
	cin>>n;
	vector<pair<pair<int,int>,int>> arr(n);
	for(int i=0;i<n;++i)
		cin>>arr[i].first.first>>arr[i].first.second>>arr[i].second;
	for(int i=0;i<n;++i)
		(arr[i].second)--;
	sort(arr.begin(),arr.end());
	array<priority_queue<int,vector<int>,greater<int>>,2> vals;
	for(int i=0;i<n;++i){
		int l = arr[i].first.first;
		int r = arr[i].first.second;
		int t = arr[i].second;
		for(int j=0;j<2;j++){
			while(!vals[j].empty() && vals[j].top()<=l){
				dp[j]++;
				vals[j].pop();
			}
		}
		for(int j=0;j<2;j++){
			while(dp[j]<dp[1-j]){
				dp[j]++;
				if(!vals[j].empty())
					vals[j].pop();
			}
		}
		vals[t].push(r);
	}
	int ans = max(dp[0]+(int)(vals[0].size()),dp[1]+(int)(vals[1].size()));
	cout<<ans;
	return 0;
}


#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[2];
int32_t main(){
	ios::sync_with_stdio(false);
    cin.tie(0);
    #ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
	freopen("output.txt","w",stdout);
	#endif
	int n;
	cin>>n;
	vector<pair<pair<int,int>,int>> arr(n);
	for(int i=0;i<n;++i)
		cin>>arr[i].first.first>>arr[i].first.second>>arr[i].second;
	for(int i=0;i<n;++i)
		(arr[i].second)--;
	sort(arr.begin(),arr.end());
	array<priority_queue<int,vector<int>,greater<int>>,2> vals;
	for(int i=0;i<n;++i){
		int l = arr[i].first.first;
		int r = arr[i].first.second;
		int t = arr[i].second;
		for(int j=0;j<2;j++){
			while(!vals[j].empty() && vals[j].top()<l){
				dp[j]++;
				vals[j].pop();
			}
		}
		for(int j=0;j<2;j++){
			while(dp[j]<dp[1-j]){
				dp[j]++;
				if(!vals[j].empty())
					vals[j].pop();
			}
		}
		vals[t].push(r);
	}
	int ans = max(dp[0]+(int)(vals[0].size()),dp[1]+(int)(vals[1].size()));
	cout<<ans;
	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define mid ((s + e) / 2)
#define makefast ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define comp(x) (lower_bound(vec.begin(), vec.end(), x) - vec.begin())
#define lc (v * 2)
#define rc (v * 2 + 1)
long long M = 1e9 + 7;
//>>>>>>>>>>>>>>>>>>>

#define int long long

const int N = 1e6 + 7;

int a[N], b[N], c[N];
int n;
int ord[N];

int seg[3][N * 4];
vector <int> spl[3];
int dp[N];
int res = 0;
int cnt[2];

int fnd(int lim, int k) {
	int l = -1, r = spl[k].size();
	while (r - l > 1) {
		int md = (r + l) / 2;
		if (b[spl[k][md]] < lim)
			l = md;
		else
			r = md;
	}
	return r;
}
int laz[3][N * 4];

void splsh(int k, int v) {
	for (int u = v * 2; u <= v * 2 + 1; u++) {
		seg[k][u] += laz[k][v];
		laz[k][u] += laz[k][v];
	}
	laz[k][v] = 0;
}

void add(int k, int l, int r, int val, int v = 1, int s = 0, int e = N) {
	if (r <= s || e <= l)
		return;
	if (l <= s && e <= r) {
		seg[k][v] += val;
		laz[k][v] += val;
		return;
	}
	splsh(k, v);
	add(k, l, r, val, lc, s, mid);
	add(k, l, r, val, rc, mid, e);
	seg[k][v] = max(seg[k][lc], seg[k][rc]);
}
int get(int k, int l, int r, int v = 1, int s = 0, int e = N) {
	if (r <= s || e <= l)
		return 0;
	if (l <= s && e <= r)
		return seg[k][v];
	splsh(k, v);
	return max(get(k, l, r, lc, s, mid), get(k, l, r, rc, mid, e));
}
int plc[N];
int32_t main() {
	makefast;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i] >> b[i] >> c[i];
		ord[i] = i;
	}
	sort (ord + 1, ord + n + 1, [](int v, int u) {
		return b[v] < b[u];
	});
	for (int i = 1; i <= n; i++) {
		plc[ord[i]] = spl[c[ord[i]]].size();
		spl[c[ord[i]]].push_back(ord[i]);
	}
	for (int i = 1; i <= n; i++) {
		int v = ord[i];
		cnt[c[v]]++;
		dp[v] = cnt[c[v]];
		int f = fnd(a[v], 3 - c[v]);
		add(3 - c[v], 0, f, 1);
		dp[v] = max(dp[v], get(3 - c[v], 0, f));
		add(c[v], plc[v], plc[v] + 1, dp[v]);
		res = max(res, dp[v]);
	//	cout << v << " > " << dp[v] << " " << get(3 - c[v], 0, v) << " " << f << " " << b[spl[3 - c[v]][f]] << endl;
	}
	cout << res;

	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define mid ((s + e) / 2)
#define makefast ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define comp(x) (lower_bound(vec.begin(), vec.end(), x) - vec.begin())
#define lc (v * 2)
#define rc (v * 2 + 1)
long long M = 1e9 + 7;
//>>>>>>>>>>>>>>>>>>>

const int N = 1e6 + 7;

int a[N], b[N], c[N];
int n;
int ord[N];

int seg[3][N * 4];
vector <int> spl[3];
int dp[N];
int res = 0;
int cnt[2];

int fnd(int lim, int k) {
	int l = -1, r = spl[k].size();
	while (r - l > 1) {
		int md = (r + l) / 2;
		if (b[spl[k][md]] < lim)
			l = md;
		else
			r = md;
	}
	return r;
}
int laz[3][N * 4];

void splsh(int k, int v) {
	for (int u = v * 2; u <= v * 2 + 1; u++) {
		seg[k][u] += laz[k][v];
		laz[k][u] += laz[k][v];
	}
	laz[k][v] = 0;
}

void add(int k, int l, int r, int val, int v = 1, int s = 0, int e = N) {
	if (r <= s || e <= l)
		return;
	if (l <= s && e <= r) {
		seg[k][v] += val;
		laz[k][v] += val;
		return;
	}
	splsh(k, v);
	add(k, l, r, val, lc, s, mid);
	add(k, l, r, val, rc, mid, e);
	seg[k][v] = max(seg[k][lc], seg[k][rc]);
}
int get(int k, int l, int r, int v = 1, int s = 0, int e = N) {
	if (r <= s || e <= l)
		return 0;
	if (l <= s && e <= r)
		return seg[k][v];
	splsh(k, v);
	return max(get(k, l, r, lc, s, mid), get(k, l, r, rc, mid, e));
}
int plc[N];
int32_t main() {
	makefast;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i] >> b[i] >> c[i];
		ord[i] = i;
	}
	sort (ord + 1, ord + n + 1, [](int v, int u) {
		return b[v] < b[u];
	});
	for (int i = 1; i <= n; i++) {
		plc[ord[i]] = spl[c[ord[i]]].size();
		spl[c[ord[i]]].push_back(ord[i]);
	}
	for (int i = 1; i <= n; i++) {
		int v = ord[i];
		cnt[c[v]]++;
		dp[v] = cnt[c[v]];
		int f = fnd(a[v], 3 - c[v]);
		add(3 - c[v], 0, f, 1);
		dp[v] = max(dp[v], get(3 - c[v], 0, f));
		add(c[v], plc[v], plc[v] + 1, dp[v]);
		res = max(res, dp[v]);
	//	cout << v << " > " << dp[v] << " " << get(3 - c[v], 0, v) << " " << f << " " << b[spl[3 - c[v]][f]] << endl;
	}
	cout << res;

	return 0;
}


#include <bits/stdc++.h>
using namespace std;
#define mid ((s + e) / 2)
#define makefast ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define comp(x) (lower_bound(vec.begin(), vec.end(), x) - vec.begin())
#define lc (v * 2)
#define rc (v * 2 + 1)
long long M = 1e9 + 7;
//>>>>>>>>>>>>>>>>>>>

const int N = 1e6 + 7;

int a[N], b[N], c[N];
int n;
int ord[N];

int seg[3][N * 4];
vector <int> spl[3];
int dp[N];
int res = 0;
int cnt[3];

int fnd(int lim, int k) {
	int l = -1, r = spl[k].size();
	while (r - l > 1) {
		int md = (r + l) / 2;
		if (b[spl[k][md]] < lim)
			l = md;
		else
			r = md;
	}
	return r;
}
int laz[3][N * 4];

void splsh(int k, int v) {
	for (int u = v * 2; u <= v * 2 + 1; u++) {
		seg[k][u] += laz[k][v];
		laz[k][u] += laz[k][v];
	}
	laz[k][v] = 0;
}

void add(int k, int l, int r, int val, int v = 1, int s = 0, int e = N) {
	if (r <= s || e <= l)
		return;
	if (l <= s && e <= r) {
		seg[k][v] += val;
		laz[k][v] += val;
		return;
	}
	splsh(k, v);
	add(k, l, r, val, lc, s, mid);
	add(k, l, r, val, rc, mid, e);
	seg[k][v] = max(seg[k][lc], seg[k][rc]);
}
int get(int k, int l, int r, int v = 1, int s = 0, int e = N) {
	if (r <= s || e <= l)
		return 0;
	if (l <= s && e <= r)
		return seg[k][v];
	splsh(k, v);
	return max(get(k, l, r, lc, s, mid), get(k, l, r, rc, mid, e));
}
int plc[N];
int32_t main() {
	makefast;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i] >> b[i] >> c[i];
		ord[i] = i;
	}
	sort (ord + 1, ord + n + 1, [](int v, int u) {
		return b[v] < b[u];
	});
	for (int i = 1; i <= n; i++) {
		plc[ord[i]] = spl[c[ord[i]]].size();
		spl[c[ord[i]]].push_back(ord[i]);
	}
	for (int i = 1; i <= n; i++) {
		int v = ord[i];
		cnt[c[v]]++;
		dp[v] = cnt[c[v]];
		int f = fnd(a[v], 3 - c[v]);
		add(3 - c[v], 0, f, 1);
		dp[v] = max(dp[v], get(3 - c[v], 0, f));
		add(c[v], plc[v], plc[v] + 1, dp[v]);
		res = max(res, dp[v]);
//		cout << v << " > " << dp[v] << " " << get(3 - c[v], 0, v) << " " << f << " " << b[spl[3 - c[v]][f]] << " " << cnt[c[v]] << endl;
	}
	cout << res;

	return 0;
}


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

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

template<typename T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
template<typename T, typename U> using ordered_map = tree<T, U, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

typedef long long ll;
typedef long double ld;
typedef double db;
typedef string str;

typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef vector<str> vstr;

#define FOR(i,j,k,in) for(int i=(j); i < (k);i+=in)
#define FORD(i,j,k,in) for(int i=(j); i >=(k);i-=in)
#define REP(i,b) FOR(i,0,b,1)
#define REPD(i,b) FORD(i,b,0,1)
#define pb push_back
#define mp make_pair
#define ff first
#define ss second
#define all(x) begin(x), end(x)
#define rsz resize

const double EPS = 1e-9;
const int MOD = 1e9+7; // 998244353;
const ll INFF = 1e18;
const int INF = 1e9;
const ld PI = acos((ld)-1);
const vi dy = {1, 0, -1, 0, -1, 1, 1, -1};
const vi dx = {0, 1, 0, -1, -1, 1, -1, 1};

#ifdef DEBUG
#define DBG if(1)
#else
#define DBG if(0)
#endif

#define dbg(x) cout << "(" << #x << " : " << x << ")" << endl;
// ostreams
template <class T, class U>
ostream& operator<<(ostream& out, const pair<T, U> &par) {out << "[" << par.first << ";" << par.second << "]"; return out;}
template <class T>
ostream& operator<<(ostream& out, const set<T> &cont) { out << "{"; for( const auto &x:cont) out << x << ", "; out << "}"; return out; }
template <class T, class U>
ostream& operator<<(ostream& out, const map<T, U> &cont) {out << "{"; for( const auto &x:cont) out << x << ", "; out << "}"; return out; }
template<class T>
ostream& operator<<(ostream& out, const vector<T> &v){ out << "[";  REP(i, v.size()) out << v[i] << ", ";  out << "]"; return out;}
// istreams
template<class T>
istream& operator>>(istream& in, vector<T> &v){ for(auto &x : v) in >> x; return in; }
template<class T, class U>
istream& operator>>(istream& in, pair<T, U> &p){ in >> p.ff >> p.ss; return in; }
//searches
template<typename T, typename U>
T bsl(T lo, T hi, U f){ hi++; T mid; while(lo < hi){ mid = (lo + hi)/2; f(mid) ? hi = mid : lo = mid+1; } return lo; }
template<typename U>
double bsld(double lo, double hi, U f, double p = 1e-9){ int r = 3 + (int)log2((hi - lo)/p); double mid; while(r--){ mid = (lo + hi)/2; f(mid) ? hi = mid : lo = mid; } return (lo + hi)/2; }
template<typename T, typename U>
T bsh(T lo, T hi, U f){ lo--; T mid; while(lo < hi){ mid = (lo + hi + 1)/2; f(mid) ? lo = mid : hi = mid-1; } return lo; }
template<typename U>
double bshd(double lo, double hi, U f, double p = 1e-9){ int r = 3+(int)log2((hi - lo)/p); double mid; while(r--){ mid = (lo + hi)/2; f(mid) ? lo = mid : hi = mid; } return (lo + hi)/2; }
// some more utility functions
template<typename T>
pair<T, int> get_min(vector<T> &v){ typename vector<T> :: iterator it = min_element(v.begin(), v.end()); return mp(*it, it - v.begin());}
template<typename T>
pair<T, int> get_max(vector<T> &v){ typename vector<T> :: iterator it = max_element(v.begin(), v.end()); return mp(*it, it - v.begin());}
template<typename T> bool ckmin(T& a, const T& b){return b < a ? a = b , true : false;}
template<typename T> bool ckmax(T& a, const T& b){return b > a ? a = b, true : false;}

struct AcAutomaton{
	void reset(){

	}
    vector<vi> segs;
    int n;
	void read_in(){
		cin >> n;
        segs.rsz(n, vi(3));
        cin >> segs;
	}
	void solve(){
		sort(all(segs));
        set<pii> open1; //red segs;
        set<pii> open2; // blue segs;
        // we are looking for matching
        int M_size = 0; // max matching size
        REP(i, segs.size()){
            vi seg = segs[i];
            while(!open1.empty() && open1.begin()->ff < seg[0]) open1.erase(open1.begin());
            while(!open2.empty() && open2.begin()->ff < seg[0]) open2.erase(open2.begin());
            if(seg[2] == 1){
                if(open2.empty()){
                    open1.insert(mp(seg[1], i));
                }
                else{
                    M_size++;
                    open2.erase(open2.begin());
                }
            }
            else{
                if(open1.empty()){
                    open2.insert(mp(seg[1] ,i));
                }
                else{
                    M_size++;
                    open1.erase(open1.begin());
                }
            }
        }
        cout << n - M_size << endl;
	}
};

int main(){
 	ios_base::sync_with_stdio(false);
 	cin.tie(NULL); cout.tie(NULL);
	int tcase = 1;
	while(tcase--){
		AcAutomaton solver;
		solver.reset();
		solver.read_in();
		solver.solve();
	}
    return 0;
}


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

#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;

template<typename T> using ordered_set = tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;
template<typename T, typename U> using ordered_map = tree<T, U, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

typedef long long ll;
typedef long double ld;
typedef double db;
typedef string str;

typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

typedef vector<int> vi;
typedef vector<ll> vll;
typedef vector<pii> vpii;
typedef vector<pll> vpll;
typedef vector<str> vstr;

#define FOR(i,j,k,in) for(int i=(j); i < (k);i+=in)
#define FORD(i,j,k,in) for(int i=(j); i >=(k);i-=in)
#define REP(i,b) FOR(i,0,b,1)
#define REPD(i,b) FORD(i,b,0,1)
#define pb push_back
#define mp make_pair
#define ff first
#define ss second
#define all(x) begin(x), end(x)
#define rsz resize

const double EPS = 1e-9;
const int MOD = 1e9+7; // 998244353;
const ll INFF = 1e18;
const int INF = 1e9;
const ld PI = acos((ld)-1);
const vi dy = {1, 0, -1, 0, -1, 1, 1, -1};
const vi dx = {0, 1, 0, -1, -1, 1, -1, 1};

#ifdef DEBUG
#define DBG if(1)
#else
#define DBG if(0)
#endif

#define dbg(x) cout << "(" << #x << " : " << x << ")" << endl;
// ostreams
template <class T, class U>
ostream& operator<<(ostream& out, const pair<T, U> &par) {out << "[" << par.first << ";" << par.second << "]"; return out;}
template <class T>
ostream& operator<<(ostream& out, const set<T> &cont) { out << "{"; for( const auto &x:cont) out << x << ", "; out << "}"; return out; }
template <class T, class U>
ostream& operator<<(ostream& out, const map<T, U> &cont) {out << "{"; for( const auto &x:cont) out << x << ", "; out << "}"; return out; }
template<class T>
ostream& operator<<(ostream& out, const vector<T> &v){ out << "[";  REP(i, v.size()) out << v[i] << ", ";  out << "]"; return out;}
// istreams
template<class T>
istream& operator>>(istream& in, vector<T> &v){ for(auto &x : v) in >> x; return in; }
template<class T, class U>
istream& operator>>(istream& in, pair<T, U> &p){ in >> p.ff >> p.ss; return in; }
//searches
template<typename T, typename U>
T bsl(T lo, T hi, U f){ hi++; T mid; while(lo < hi){ mid = (lo + hi)/2; f(mid) ? hi = mid : lo = mid+1; } return lo; }
template<typename U>
double bsld(double lo, double hi, U f, double p = 1e-9){ int r = 3 + (int)log2((hi - lo)/p); double mid; while(r--){ mid = (lo + hi)/2; f(mid) ? hi = mid : lo = mid; } return (lo + hi)/2; }
template<typename T, typename U>
T bsh(T lo, T hi, U f){ lo--; T mid; while(lo < hi){ mid = (lo + hi + 1)/2; f(mid) ? lo = mid : hi = mid-1; } return lo; }
template<typename U>
double bshd(double lo, double hi, U f, double p = 1e-9){ int r = 3+(int)log2((hi - lo)/p); double mid; while(r--){ mid = (lo + hi)/2; f(mid) ? lo = mid : hi = mid; } return (lo + hi)/2; }
// some more utility functions
template<typename T>
pair<T, int> get_min(vector<T> &v){ typename vector<T> :: iterator it = min_element(v.begin(), v.end()); return mp(*it, it - v.begin());}
template<typename T>
pair<T, int> get_max(vector<T> &v){ typename vector<T> :: iterator it = max_element(v.begin(), v.end()); return mp(*it, it - v.begin());}
template<typename T> bool ckmin(T& a, const T& b){return b < a ? a = b , true : false;}
template<typename T> bool ckmax(T& a, const T& b){return b > a ? a = b, true : false;}

struct AcAutomaton{
	void reset(){

	}
    vector<vi> segs;
    int n;
	void read_in(){
		cin >> n;
        segs.rsz(n, vi(3));
        cin >> segs;
	}
	void solve(){
		sort(all(segs));
        set<pii> open1; //red segs;
        set<pii> open2; // blue segs;
        // we are looking for matching
        int M_size = 0; // max matching size
        vector<vi> evs;
        REP(i, segs.size()){
            evs.pb({segs[i][0], 1, i});
            evs.pb({segs[i][1], 2, i});
        }
        sort(all(evs));
        REP(i, evs.size()){
            vi seg = segs[evs[i][2]];
            while(!open1.empty() && open1.begin()->ff < seg[0]) open1.erase(open1.begin());
            while(!open2.empty() && open2.begin()->ff < seg[0]) open2.erase(open2.begin());
            if(evs[i][1] == 1){
                if(seg[2] == 1) open1.insert(mp(seg[1], evs[i][2]));
                else open2.insert(mp(seg[1], evs[i][2]));
            }
            else{
                if(seg[2] == 1 && open1.find(mp(seg[1], evs[i][2])) == open1.end()) continue;
                if(seg[2] == 2 && open2.find(mp(seg[1], evs[i][2])) == open2.end()) continue;
                if(seg[2] == 1){
                    if(!open2.empty()){
                        M_size++;
                        open2.erase(open2.begin());
                        open1.erase(mp(seg[1], evs[i][2]));
                    }
                }
                else{
                    if(!open1.empty()){
                        M_size++;
                        open1.erase(open1.begin());
                        open2.erase(mp(seg[1], evs[i][2]));
                    }
                }
            }
        }
        cout << n - M_size << endl;
	}
};

int main(){
 	ios_base::sync_with_stdio(false);
 	cin.tie(NULL); cout.tie(NULL);
	int tcase = 1;
	while(tcase--){
		AcAutomaton solver;
		solver.reset();
		solver.read_in();
		solver.solve();
	}
    return 0;
}


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

pair<pair<int, int>, int> p[200002];
int l[200002], r[200002], t[200002];

struct seg{
    int seg[1600002], lazy[1600002];
    void push(int n, int s, int e, int val){
        seg[n] += val;
        if(s != e){
            lazy[n+n] += val;
            lazy[n+n+1] += val;
        }
        lazy[n] = 0;
    }
    void update(int n, int s, int e, int l, int r, int v){
        push(n, s, e, lazy[n]);
        if(s > r || l > e) return;
        if(l <= s && e <= r){
            push(n, s, e, v);
            return;
        }
        int mid = (s+e)/2;
        update(n+n, s, mid, l, r, v);
        update(n+n+1, mid+1, e, l, r, v);
        seg[n] = max(seg[n+n], seg[n+n+1]);
    }
    int query(int n, int s, int e, int l, int r){
        push(n, s, e, lazy[n]);
        if(s > r || l > e) return 0;
        if(l <= s && e <= r) return seg[n];
        int mid = (s+e)/2;
        return max(query(n+n, s, mid, l, r), query(n+n+1, mid+1, e, l, r));
    }
}seg[4];

int main(){
    int n;
    scanf("%d", &n);
    vector<int> x;
    x.emplace_back(0);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d", &l[i], &r[i], &t[i]);
        t[i]--;
        x.emplace_back(l[i]);
        x.emplace_back(r[i]);
        p[i] = make_pair(make_pair(r[i], -l[i]), t[i]);
    }
    sort(x.begin(), x.end());
    x.erase(unique(x.begin(), x.end()), x.end());
    int m = x.size();
    sort(p+1, p+1+n);
    int ans = 0;
    for(int i=1;i<=n;i++){
        int l = -p[i].first.second, r = p[i].first.first, t = p[i].second;
        l = lower_bound(x.begin(), x.end(), l) - x.begin() + 1;
        r = lower_bound(x.begin(), x.end(), r) - x.begin() + 1;

        int temp = seg[1-t].query(1, 1, m, 1, l-1) + 1;
        temp = max(temp, seg[2+t].query(1, 1, m, 1, l-1) + 1);

        int q = seg[t].query(1, 1, m, r, r);
        if(q < temp) seg[t].update(1, 1, m, r, r, -q+temp);
        q = seg[2+t].query(1, 1, m, r, r);
        if(q < temp) seg[2+t].update(1, 1, m, r, r, -q+temp);

        seg[1-t].update(1, 1, m, 1, l-1, 1);
        seg[2+t].update(1, 1, m, 1, l-1, 1);
        ans = max(ans, temp);
    }
    printf("%d\n", ans);
}


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

pair<pair<int, int>, int> p[200002];
int l[200002], r[200002], t[200002];

struct seg{
    int seg[800002], lazy[800002];
    void push(int n, int s, int e, int val){
        seg[n] += val;
        if(s != e){
            lazy[n+n] += val;
            lazy[n+n+1] += val;
        }
        lazy[n] = 0;
    }
    void update(int n, int s, int e, int l, int r, int v){
        push(n, s, e, lazy[n]);
        if(s > r || l > e) return;
        if(l <= s && e <= r){
            push(n, s, e, v);
            return;
        }
        int mid = (s+e)/2;
        update(n+n, s, mid, l, r, v);
        update(n+n+1, mid+1, e, l, r, v);
        seg[n] = max(seg[n+n], seg[n+n+1]);
    }
    int query(int n, int s, int e, int l, int r){
        push(n, s, e, lazy[n]);
        if(s > r || l > e) return 0;
        if(l <= s && e <= r) return seg[n];
        int mid = (s+e)/2;
        return max(query(n+n, s, mid, l, r), query(n+n+1, mid+1, e, l, r));
    }
}seg[4];

int main(){
    int n;
    scanf("%d", &n);
    vector<int> x;
    x.emplace_back(0);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d", &l[i], &r[i], &t[i]);
        t[i]--;
        x.emplace_back(l[i]);
        x.emplace_back(r[i]);
        p[i] = make_pair(make_pair(r[i], -l[i]), t[i]);
    }
    sort(x.begin(), x.end());
    x.erase(unique(x.begin(), x.end()), x.end());
    int m = x.size();
    sort(p+1, p+1+n);
    int ans = 0;
    for(int i=1;i<=n;i++){
        int l = -p[i].first.second, r = p[i].first.first, t = p[i].second;
        l = lower_bound(x.begin(), x.end(), l) - x.begin() + 1;
        r = lower_bound(x.begin(), x.end(), r) - x.begin() + 1;

        int temp = seg[1-t].query(1, 1, m, 1, l-1) + 1;
        temp = max(temp, seg[2+t].query(1, 1, m, 1, l-1) + 1);

        int q = seg[t].query(1, 1, m, r, r);
        if(q < temp) seg[t].update(1, 1, m, r, r, -q+temp);
        q = seg[2+t].query(1, 1, m, r, r);
        if(q < temp) seg[2+t].update(1, 1, m, r, r, -q+temp);

        seg[1-t].update(1, 1, m, 1, l-1, 1);
        seg[2+t].update(1, 1, m, 1, l-1, 1);
        ans = max(ans, temp);
    }
    printf("%d\n", ans);
}


#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <cstring>
//#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define IOS ios::sync_with_stdio(0);cin.tie(0);
#define de(x,y) cout<<#x<<" :"<<x<<y;
#define int long long
#define SZ(xx) ((int)xx.size())
#define lowbit(xx) (xx&(-xx))
#define pb push_back
#define ls node + node
#define rs node + node + 1
typedef pair<int,int> pii;

const int maxn = 4e5+5;

struct Line {
	int l, r, ty;
} pt[maxn];

vector<pii> vec[maxn];
int dp[maxn][3] = {};

struct Segment_Tree {
	int seg[maxn] = {};

	void upd(int node,int l,int r,int x,int val) {
		if (l == x && r == x) {
			seg[node] = max(seg[node], val);
		}
		else {
			int m = (l + r) >> 1;
			if (x <= m) upd(ls, l, m, x, val);
			else upd(rs, m+1, r, x, val);
			seg[node] = max(seg[ls], seg[rs]);
		}
	}

	int qry(int node,int l,int r,int ql,int qr) {
		if (ql <= l && r <= qr) return seg[node];
		int m = (l + r) >> 1, ret = 0;
		if (ql <= m) ret = max(ret, qry(ls, l, m, ql, qr));
		if (qr > m) ret = max(ret, qry(rs, m+1, r, ql, qr));
		return ret;
	}
} Seg[3];
main() {
 	IOS
	int n;
	cin >> n;
	vector<int> ox;
	for (int i = 1; i <= n; i++) {
		cin >> pt[i].l >> pt[i].r >> pt[i].ty;
		ox.pb(pt[i].l);
		ox.pb(pt[i].r);
	}

	ox.push_back(0);
	sort(ox.begin(), ox.end());
	ox.resize(unique(ox.begin(), ox.end()) - ox.begin());

	for (int i = 1; i <= n; i++) {
		pt[i].l = lower_bound(ox.begin(), ox.end(), pt[i].l) - ox.begin() + 1;
		pt[i].r = lower_bound(ox.begin(), ox.end(), pt[i].r) - ox.begin() + 1;
		vec[pt[i].r].pb({pt[i].l, pt[i].ty});
	}

	for (int i = 1;  i <= SZ(ox); i++) {
		sort (vec[i].begin(), vec[i].end(), [](pii a, pii b) {
			return a.X > b.X;
		});
		int cnt[3] = {};
		for (auto k:vec[i]) {
			if (k.Y == 1) {
				cnt[1]++;
				int val =  max(Seg[1].qry(1, 1, SZ(ox), 1, i - 1), Seg[2].qry(1, 1, SZ(ox), 1, k.X - 1)) + cnt[1];
				Seg[1].upd(1, 1, SZ(ox), i, val);
			}
			else {
				cnt[2]++;
				int val =  max(Seg[2].qry(1, 1, SZ(ox), 1, i - 1), Seg[1].qry(1, 1, SZ(ox), 1, k.X - 1)) + cnt[2];
				Seg[2].upd(1, 1, SZ(ox), i, val);
			}
		}
	}
	cout << max(Seg[1].qry(1, 1, SZ(ox), 1, SZ(ox)), Seg[2].qry(1, 1, SZ(ox), 1, SZ(ox))) << "\n";

}


#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <cstring>
//#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define IOS ios::sync_with_stdio(0);cin.tie(0);
#define de(x,y) cout<<#x<<" :"<<x<<y;
#define int long long
#define SZ(xx) ((int)xx.size())
#define lowbit(xx) (xx&(-xx))
#define pb push_back
#define ls node + node
#define rs node + node + 1
typedef pair<int,int> pii;

const int maxn = 4e5+5;

struct Line {
	int l, r, ty;
} pt[maxn];

vector<pii> vec[maxn];
int Same[maxn][3] = {};

struct Segment_Tree {
	int seg[maxn] = {};

	void upd(int node,int l,int r,int x,int val) {
		if (l == x && r == x) {
			seg[node] = max(seg[node], val);
		}
		else {
			int m = (l + r) >> 1;
			if (x <= m) upd(ls, l, m, x, val);
			else upd(rs, m+1, r, x, val);
			seg[node] = max(seg[ls], seg[rs]);
		}
	}

	int qry(int node,int l,int r,int ql,int qr) {
		if (ql <= l && r <= qr) return seg[node];
		int m = (l + r) >> 1, ret = 0;
		if (ql <= m) ret = max(ret, qry(ls, l, m, ql, qr));
		if (qr > m) ret = max(ret, qry(rs, m+1, r, ql, qr));
		return ret;
	}
} Seg[3];

struct Bit {
	int bitR[maxn] = {}, bitL[maxn] = {};

	void add(int l,int r) {
		for (int i = r ; i < maxn; i += lowbit(i))
			bitR[i] += 1;
		for (int i = l - 1 ; i < maxn; i += lowbit(i))
			bitL[i] += 1;
	}

	int qry(int R, int L) {
		int ret = 0;
		for (int i = R; i > 0; i -= lowbit(i))
			ret += bitR[i];
		for (int i = L - 1; i > 0; i -= lowbit(i))
			ret -= bitL[i];
		return ret;
	}
} bit[3];

main() {
 	IOS
	int n;
	cin >> n;
	vector<int> ox;
	for (int i = 1; i <= n; i++) {
		cin >> pt[i].l >> pt[i].r >> pt[i].ty;
		ox.pb(pt[i].l);
		ox.pb(pt[i].r);
	}

	ox.push_back(0);
	sort(ox.begin(), ox.end());
	ox.resize(unique(ox.begin(), ox.end()) - ox.begin());

	for (int i = 1; i <= n; i++) {
		pt[i].l = lower_bound(ox.begin(), ox.end(), pt[i].l) - ox.begin() + 1;
		pt[i].r = lower_bound(ox.begin(), ox.end(), pt[i].r) - ox.begin() + 1;
		vec[pt[i].r].pb({pt[i].l, pt[i].ty});
	}

	for (int i = 1;  i <= SZ(ox); i++) {
		sort (vec[i].begin(), vec[i].end(), [](pii a, pii b) {
			return a.X > b.X;
		});
		for (auto a:vec[i]) {
			if (a.Y == 1) {
				bit[1].add(a.X, i);
			}
			else {
				bit[2].add(a.X, i);
			}
			if (a.X == i) {
				Same[i][a.Y]++;
			}
		}
		for (auto a:vec[i]) {
			int val;
			if (a.Y == 1) {
				val = max(Seg[1].qry(1, 1, SZ(ox), 1, a.X) + bit[1].qry(i, a.X - 1) - Same[a.X][1], Seg[2].qry(1, 1, SZ(ox), 1, a.X - 1) + bit[1].qry(i, a.X - 1));
				Seg[1].upd(1, 1, SZ(ox), i, val);
			}
			else {
				val = max(Seg[1].qry(1, 1, SZ(ox), 1, a.X - 1) + bit[2].qry(i, a.X - 1), Seg[2].qry(1, 1, SZ(ox), 1, a.X) + bit[2].qry(i, a.X - 1) - Same[a.X][2]);
				Seg[2].upd(1, 1, SZ(ox), i, val);

			}
		}
//		cout << i << " " << max(Seg[1].qry(1, 1, SZ(ox), i, i), Seg[2].qry(1, 1, SZ(ox), i, i)) << "\n";
	}
	cout << max(Seg[1].qry(1, 1, SZ(ox), 1, SZ(ox)), Seg[2].qry(1, 1, SZ(ox), 1, SZ(ox))) << "\n";

}


#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <cstring>
//#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define IOS ios::sync_with_stdio(0);cin.tie(0);
#define de(x,y) cout<<#x<<" :"<<x<<y;
#define int long long
#define SZ(xx) ((int)xx.size())
#define lowbit(xx) (xx&(-xx))
#define pb push_back
#define ls node + node
#define rs node + node + 1
typedef pair<int,int> pii;

const int maxn = 4e5+5;

struct Line {
	int l, r, ty;
} pt[maxn];

vector<pii> vec[maxn];
int dp[maxn][3] = {};

struct Segment_Tree {
	int seg[maxn] = {};

	void upd(int node,int l,int r,int x,int val) {
		if (l == x && r == x) {
			seg[node] = max(seg[node], val);
		}
		else {
			int m = (l + r) >> 1;
			if (x <= m) upd(ls, l, m, x, val);
			else upd(rs, m+1, r, x, val);
			seg[node] = max(seg[ls], seg[rs]);
		}
	}

	int qry(int node,int l,int r,int ql,int qr) {
		if (ql <= l && r <= qr) return seg[node];
		int m = (l + r) >> 1, ret = 0;
		if (ql <= m) ret = max(ret, qry(ls, l, m, ql, qr));
		if (qr > m) ret = max(ret, qry(rs, m+1, r, ql, qr));
		return ret;
	}
} Seg[3];
main() {
 	IOS
	int n;
	cin >> n;
	vector<int> ox;
	for (int i = 1; i <= n; i++) {
		cin >> pt[i].l >> pt[i].r >> pt[i].ty;
		ox.pb(pt[i].l);
		ox.pb(pt[i].r);
	}

	ox.push_back(0);
	sort(ox.begin(), ox.end());
	ox.resize(unique(ox.begin(), ox.end()) - ox.begin());

	for (int i = 1; i <= n; i++) {
		pt[i].l = lower_bound(ox.begin(), ox.end(), pt[i].l) - ox.begin() + 1;
		pt[i].r = lower_bound(ox.begin(), ox.end(), pt[i].r) - ox.begin() + 1;
		vec[pt[i].r].pb({pt[i].l, pt[i].ty});
	}

	for (int i = 1;  i <= SZ(ox); i++) {
		sort (vec[i].begin(), vec[i].end(), [](pii a, pii b) {
			return a.X < b.X;
		});
		int cnt[3] = {};
		for (auto k:vec[i]) {
			if (k.Y == 1) {
				cnt[1]++;
				int val =  max(Seg[1].qry(1, 1, SZ(ox), 1, i - 1), Seg[2].qry(1, 1, SZ(ox), 1, k.X - 1)) + cnt[1];
				Seg[1].upd(1, 1, SZ(ox), i, val);
			}
			else {
				cnt[2]++;
				int val =  max(Seg[2].qry(1, 1, SZ(ox), 1, i - 1), Seg[1].qry(1, 1, SZ(ox), 1, k.X - 1)) + cnt[2];
				Seg[2].upd(1, 1, SZ(ox), i, val);
			}
		}
	}
	cout << max(Seg[1].qry(1, 1, SZ(ox), 1, SZ(ox)), Seg[2].qry(1, 1, SZ(ox), 1, SZ(ox))) << "\n";

}


#include <iostream>
#include <algorithm>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <deque>
#include <cstring>
//#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define IOS ios::sync_with_stdio(0);cin.tie(0);
#define de(x,y) cout<<#x<<" :"<<x<<y;
#define int long long
#define SZ(xx) ((int)xx.size())
#define lowbit(xx) (xx&(-xx))
#define pb push_back
#define ls node + node
#define rs node + node + 1
typedef pair<int,int> pii;

const int maxn = 4e5+5;

struct Line {
	int l, r, ty;
} pt[maxn];

vector<pii> vec[maxn];
int Same[maxn][3] = {};

struct Segment_Tree {
	int seg[maxn * 4] = {}, tag[maxn * 4] = {};

	void push(int node) {
		seg[ls] += tag[node];
		seg[rs] += tag[node];
		tag[ls] += tag[node];
		tag[rs] += tag[node];
		tag[node] = 0;
	}

	void upd1(int node,int l,int r,int x,int val) {
		if (x == l && r == x) {
			seg[node] = max(seg[node], val);
		}
		else {
			push(node);
			int m = (l + r) >> 1;
			if (x <= m) upd1(ls, l, m, x, val);
			else upd1(rs, m+1, r, x, val);
			seg[node] = max(seg[ls], seg[rs]);
		}
	}

	void upd2(int node,int l,int r,int ql,int qr,int val) {
		if (ql <= l && r <= qr) {
			seg[node] += val;
			tag[node] += val;
		}
		else {
			push(node);
			int m = (l + r) >> 1;
			if (ql <= m) upd2(ls, l, m, ql, qr, val);
			if (qr > m) upd2(rs, m+1, r, ql, qr, val);
			seg[node] = max(seg[ls], seg[rs]);
		}
	}

	int qry(int node,int l,int r,int ql,int qr) {
		if (ql <= l && r <= qr) return seg[node];
		push(node);
		int m = (l + r) >> 1, ret = 0;
		if (ql <= m) ret = max(ret, qry(ls, l, m, ql, qr));
		if (qr > m) ret = max(ret, qry(rs, m+1, r, ql, qr));
		return ret;
	}

} Seg[3];

main() {
 	IOS
	int n;
	cin >> n;
	vector<int> ox;
	for (int i = 1; i <= n; i++) {
		cin >> pt[i].l >> pt[i].r >> pt[i].ty;
		pt[i].ty--;
		ox.pb(pt[i].l);
		ox.pb(pt[i].r);
	}

	ox.push_back(0);
	sort(ox.begin(), ox.end());
	ox.resize(unique(ox.begin(), ox.end()) - ox.begin());

	for (int i = 1; i <= n; i++) {
		pt[i].l = lower_bound(ox.begin(), ox.end(), pt[i].l) - ox.begin() + 1;
		pt[i].r = lower_bound(ox.begin(), ox.end(), pt[i].r) - ox.begin() + 1;
		vec[pt[i].r].pb({pt[i].l, pt[i].ty});
	}

	for (int i = 1;  i <= SZ(ox); i++) {
		sort (vec[i].begin(), vec[i].end(), [](pii a, pii b) {
			return a.X > b.X;
		});
		for (auto a:vec[i]) {
			Seg[a.Y ^ 1].upd2(1, 1, SZ(ox), 1, a.X - 1, 1);
		}

		for (auto a:vec[i]) {
			int tmp = Seg[a.Y ^ 1].qry(1, 1, SZ(ox), 1, a.X - 1);
			Seg[a.Y].upd1(1, 1, SZ(ox), i, tmp);
		}
//		cout << i << " " << max(Seg[0].qry(1, 1, SZ(ox), i, i), Seg[1].qry(1, 1, SZ(ox), i, i)) << "\n";
	}
	cout << max(Seg[0].qry(1, 1, SZ(ox), 1, SZ(ox)), Seg[1].qry(1, 1, SZ(ox), 1, SZ(ox))) << "\n";

}


#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#define pii pair<int, int>
#define piii pair<int, pii>
#define pll pair<long long, long long>
#define plll pair<long long, pll>
#define ff first
#define ss second
#define ee ss.ff
#define rr ss.ss

using namespace std;

struct SEG
{
    int seg[808080]{};
    int lazy[808080]{};
    void prop(int ind)
    {
        if(lazy[ind])
        {
            seg[ind << 1] += lazy[ind];
            lazy[ind << 1] += lazy[ind];
            seg[ind << 1 | 1] += lazy[ind];
            lazy[ind << 1 | 1] += lazy[ind];
            lazy[ind] = 0;
        }
    }
    int qr(int s, int e, int ind, int l, int r)
    {
        if(e <= l || r <= s) return 0;
        if(l <= s && e <= r) return seg[ind];

        prop(ind);
        int mid = s + e >> 1;
        return max(qr(s, mid, ind << 1, l, r), qr(mid, e, ind << 1 | 1, l, r));
    }
    void upd(int s, int e, int ind, int l, int r, int x)
    {
        if(e <= l || r <= s) return;
        if(l <= s && e <= r)
        {
            seg[ind] += x;
            lazy[ind] += x;
            return;
        }

        prop(ind);
        int mid = s + e >> 1;
        upd(s, mid, ind << 1, l, r, x);
        upd(mid, e, ind << 1 | 1, l, r, x);
        seg[ind] = max(seg[ind << 1], seg[ind << 1 | 1]);
    }
}A, B;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; cin >> n;
    piii arr[n]; for(auto &i : arr) cin >> i.ff >> i.ee >> i.rr;
    vector<int> pr; for(auto &i : arr) pr.push_back(i.ff), pr.push_back(i.ee);
    sort(pr.begin(), pr.end()); pr.erase(unique(pr.begin(), pr.end()), pr.end());
    for(auto &i : arr) i.ff = lower_bound(pr.begin(), pr.end(), i.ff) - pr.begin(), i.ee = lower_bound(pr.begin(), pr.end(), i.ee) - pr.begin();
    int N = pr.size();

    vector<int> X[N], Y[N];
    for(auto &i : arr)
    {
        if(i.rr == 1) X[i.ee].push_back(i.ff);
        else Y[i.ee].push_back(i.ff);
    }

    for(int i = 0; i < N; ++i)
    {
        for(auto j : X[i]) B.upd(0, N + 1, 1, 0, j + 1, 1);
        for(auto j : Y[i]) A.upd(0, N + 1, 1, 0, j + 1, 1);
        A.upd(0, N + 1, 1, i + 1, i + 2, max(A.qr(0, N + 1, 1, i, i + 1), B.qr(0, N + 1, 1, 0, i + 1)));
        B.upd(0, N + 1, 1, i + 1, i + 2, max(B.qr(0, N + 1, 1, i, i + 1), A.qr(0, N + 1, 1, 0, i + 1)));
    }

    cout << max(A.qr(0, N + 1, 1, N, N + 1), B.qr(0, N + 1, 1, N, N + 1));
}


#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#define pii pair<int, int>
#define piii pair<int, pii>
#define pll pair<long long, long long>
#define plll pair<long long, pll>
#define ff first
#define ss second
#define ee ss.ff
#define rr ss.ss

using namespace std;

struct SEG
{
    int seg[1616161]{};
    int lazy[1616161]{};
    void prop(int ind)
    {
        if(lazy[ind])
        {
            seg[ind << 1] += lazy[ind];
            lazy[ind << 1] += lazy[ind];
            seg[ind << 1 | 1] += lazy[ind];
            lazy[ind << 1 | 1] += lazy[ind];
            lazy[ind] = 0;
        }
    }
    int qr(int s, int e, int ind, int l, int r)
    {
        if(e <= l || r <= s) return 0;
        if(l <= s && e <= r) return seg[ind];

        prop(ind);
        int mid = s + e >> 1;
        return max(qr(s, mid, ind << 1, l, r), qr(mid, e, ind << 1 | 1, l, r));
    }
    void upd(int s, int e, int ind, int l, int r, int x)
    {
        if(e <= l || r <= s) return;
        if(l <= s && e <= r)
        {
            seg[ind] += x;
            lazy[ind] += x;
            return;
        }

        prop(ind);
        int mid = s + e >> 1;
        upd(s, mid, ind << 1, l, r, x);
        upd(mid, e, ind << 1 | 1, l, r, x);
        seg[ind] = max(seg[ind << 1], seg[ind << 1 | 1]);
    }
}A, B;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n; cin >> n;
    piii arr[n]; for(auto &i : arr) cin >> i.ff >> i.ee >> i.rr;
    vector<int> pr; for(auto &i : arr) pr.push_back(i.ff), pr.push_back(i.ee);
    sort(pr.begin(), pr.end()); pr.erase(unique(pr.begin(), pr.end()), pr.end());
    for(auto &i : arr) i.ff = lower_bound(pr.begin(), pr.end(), i.ff) - pr.begin(), i.ee = lower_bound(pr.begin(), pr.end(), i.ee) - pr.begin();
    int N = pr.size();

    vector<int> X[N], Y[N];
    for(auto &i : arr)
    {
        if(i.rr == 1) X[i.ee].push_back(i.ff);
        else Y[i.ee].push_back(i.ff);
    }

    for(int i = 0; i < N; ++i)
    {
        for(auto j : X[i]) B.upd(0, N + 1, 1, 0, j + 1, 1);
        for(auto j : Y[i]) A.upd(0, N + 1, 1, 0, j + 1, 1);
        A.upd(0, N + 1, 1, i + 1, i + 2, max(A.qr(0, N + 1, 1, i, i + 1), B.qr(0, N + 1, 1, 0, i + 1)));
        B.upd(0, N + 1, 1, i + 1, i + 2, max(B.qr(0, N + 1, 1, i, i + 1), A.qr(0, N + 1, 1, 0, i + 1)));
    }

    cout << max(A.qr(0, N + 1, 1, N, N + 1), B.qr(0, N + 1, 1, N, N + 1));
}


#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct seg
{
    int l, r, c;
};
bool right_cmp(const seg &l, const seg &r)
{
    return l.r < r.r;
}
int size_pad(int x)
{
    while(x & (x - 1)) x += x & -x;
    return x;
}
struct segtree
{
    const int size;
    vector<int> data;
    vector<int> lazy;
    int cx;
    segtree(int n) : size(n), data(size * 4 + 5), lazy(size * 4 + 5) {}
    void propg(int id, int leaf)
    {
        if(!leaf){
          data[id << 1] += lazy[id];
          lazy[id << 1] += lazy[id];
          data[id << 1 | 1] += lazy[id];
          lazy[id << 1 | 1] += lazy[id];
        }
        lazy[id] = 0;
    }
    void upd(int id, int l, int r)
    {
        if(r <= cx){
          ++lazy[id];
          ++data[id];
          propg(id, l == r);
          return;
        }
        propg(id, 0);
        int mid = (l + r) >> 1;
        upd(id << 1, l, mid);
        if(cx > mid) upd(id << 1 | 1, mid + 1, r);
        data[id] = max(data[id << 1], data[id << 1 | 1]);
    }
    int get(int id, int l, int r)
    {
        propg(id, l == r);
        if(r <= cx) return data[id];
        int resl = 0, resr = 0;
        int mid = (l + r) >> 1;
        resl = get(id << 1, l, mid);
        if(cx > mid) resr = get(id << 1 | 1, mid + 1, r);
        return max(resl, resr);
    }
    void upd(int x)
    {
        cx = x;
        upd(1, 0, size);
    }
    void set(int x, int v)
    {
        int id = 1, l = 0, r = size;
        while(l != r){
          propg(id, 0);
          int mid = (l + r) >> 1;
          if(x > mid){
            id = id << 1 | 1;
            l = mid + 1;
          }
          else{
            id = id << 1;
            r = mid;
          }
        }
        data[id] = v;
        for(id >>= 1; id; id >>= 1)
          data[id] = max(data[id << 1], data[id << 1 | 1]);
    }
    int get(int x)
    {
        cx = x;
        return get(1, 0, size);
    }
};
#define nmax 200000
int n, s[2][nmax+1], n_[2];
seg a[nmax+1];
int main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(nullptr);
    cin >> n;
    for(int i = 1; i <= n; ++i){
      int l, r, c;
      cin >> l >> r >> c;
      --c;
      a[i] = {l, r, c};
      s[c][++n_[c]] = r;
    }
    segtree sgt[2]{n_[0], n_[1]};
    s[0][0] = s[1][0] = 0;
    std::sort(a + 1, a + n + 1, right_cmp);
    for(int i = 0; i < 2; ++i)
      std::sort(s[i] + 1, s[i] + n_[i] + 1);
    int ans = 0;
    for(int i = 1, c[2]{}; i <= n; ++i){
      ++c[a[i].c];
      int idx = std::lower_bound(s[!a[i].c] + 1, s[!a[i].c] + n_[!a[i].c] + 1, a[i].l) - s[!a[i].c] - 1;
      sgt[!a[i].c].upd(idx);
      int tmp = sgt[!a[i].c].get(idx);
      ans = std::max(ans, tmp);
      sgt[a[i].c].set(c[a[i].c], tmp);
    }
    cout << ans;
    return 0;
}