#include<iostream>
#include<vector>
#include<algorithm>
#include<cassert>
#define ull unsigned long long
using namespace std;
const int N = 1e5 + 10;
const int m = 1e5 + 5;
struct Op {
int x, y1, y2, id;
inline bool operator<(const Op &b) const {
if(x != b.x) return x < b.x;
return id < b.id;
}
};
struct Range {
int l, r;
};
int n, ans;
int col[N], exist[N];
vector<Op> op;
Range a[N];
vector<int> pos[N];
struct hash_table {
static const int LOGN = 18;
static const int MOD = 10000079;
struct Node {
ull key; int val, next;
} pool[4 * N * LOGN];
int nn, head[MOD];
inline hash_table() { nn = 0; for(int i = 0; i < MOD; i++) head[i] = 0; }
inline bool add(ull key, int v = 1) {
for(int i = head[key % MOD]; i; i = pool[i].next) {
if(pool[i].key == key) {
if(pool[i].val) return pool[i].val += v, false;
else return pool[i].val += v, true;
}
}
pool[++nn] = {key, v, head[key % MOD]};
head[key % MOD] = nn;
return true;
}
inline void erase(ull key) {
for(int i = head[key % MOD]; i; i = pool[i].next) {
if(pool[i].key == key) return pool[i].val--, void();
}
assert(false);
}
inline void clear(ull key) {
for(int i = head[key % MOD]; i; i = pool[i].next) {
if(pool[i].key == key) return pool[i].val = 0, void();
}
// assert(false);
}
inline int query(ull key) {
for(int i = head[key % MOD]; i; i = pool[i].next) {
if(pool[i].key == key) return pool[i].val;
}
return 0;
}
} st1, st2;
namespace SegT {
struct Node {
ull p;
vector<int> buf1, buf2;
inline int cnt1(int c) { return st1.query(p | c); }
inline void ins1(int c) { if(st1.add(p | c)) buf1.push_back(c); }
inline void era1(int c) { st1.erase(p | c); }
inline int cnt2(int c) { return st2.query(p | c); }
inline void ins2(int c) { if(st2.add(p | c)) buf2.push_back(c); }
inline void era2(int c) { st2.erase(p | c); }
inline void merge1(int x, int y) {
if(st1.add(p | x, st1.query(p | y))) buf1.push_back(x);
st1.clear(p | y);
}
inline void merge2(int x, int y) {
if(st2.add(p | x, st2.query(p | y))) buf2.push_back(x);
st2.clear(p | y);
}
} tr[4 * N];
inline int lc(int x) { return x << 1; }
inline int rc(int x) { return x << 1 | 1; }
void build(int p, int l, int r) {
tr[p].p = (ull)p << 32;
if(l == r) return;
int mid = (l + r) >> 1;
build(lc(p), l, mid);
build(rc(p), mid + 1, r);
}
void merge(int p, int l, int r, int ql, int qr, int x, int y) {
if(ql <= l && r <= qr) return tr[p].merge2(x, y);
tr[p].merge1(x, y);
int mid = (l + r) >> 1;
if(ql <= mid) merge(lc(p), l, mid, ql, qr, x, y);
if(mid < qr) merge(rc(p), mid + 1, r, ql, qr, x, y);
}
void merge(int x, int y) {
if(x == y || !exist[x] || !exist[y]) return;
if(pos[x].size() < pos[y].size()) swap(x, y);
for(int i : pos[y]) {
merge(1, 1, m, a[i].l, a[i].r, x, y);
col[i] = x;
pos[x].push_back(i);
}
vector<int>().swap(pos[y]);
exist[y] = 0;
}
void insert(int p, int l, int r, int ql, int qr, int x) {
if(ql <= l && r <= qr) return tr[p].ins2(x);
tr[p].ins1(x);
int mid = (l + r) >> 1;
if(ql <= mid) insert(lc(p), l, mid, ql, qr, x);
if(mid < qr) insert(rc(p), mid + 1, r, ql, qr, x);
}
void assign(int p, int l, int r, int ql, int qr, int x) {
if(ql <= l && r <= qr) {
for(int ii = 0; ii < tr[p].buf1.size(); ii++)
if(tr[p].cnt1(tr[p].buf1[ii])) merge(col[x], tr[p].buf1[ii]);
for(int ii = 0; ii < tr[p].buf2.size(); ii++)
if(tr[p].cnt2(tr[p].buf2[ii])) merge(col[x], tr[p].buf2[ii]);
tr[p].buf1.clear(); tr[p].buf1.push_back(col[x]);
tr[p].buf2.clear(); tr[p].buf2.push_back(col[x]);
return;
}
for(int ii = 0; ii < tr[p].buf2.size(); ii++)
if(tr[p].cnt2(tr[p].buf2[ii])) merge(col[x], tr[p].buf2[ii]);
tr[p].buf2.clear(); tr[p].buf2.push_back(col[x]);
int mid = (l + r) >> 1;
if(ql <= mid) assign(lc(p), l, mid, ql, qr, x);
if(mid < qr) assign(rc(p), mid + 1, r, ql, qr, x);
}
void erase(int p, int l, int r, int ql, int qr, int x) {
if(ql <= l && r <= qr) return tr[p].era2(col[x]);
tr[p].era1(col[x]);
int mid = (l + r) >> 1;
if(ql <= mid) erase(lc(p), l, mid, ql, qr, x);
if(mid < qr) erase(rc(p), mid + 1, r, ql, qr, x);
}
}
void insert(int l, int r, int id) {
SegT::insert(1, 1, m, l, r, id);
SegT::assign(1, 1, m, l, r, id);
}
void erase(int l, int r, int id) {
SegT::erase(1, 1, m, l, r, id);
}
int main() {
freopen("ding.in", "r", stdin);
freopen("ding.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
op.push_back({x1, y1, y2, i});
op.push_back({x2 + 1, y1, y2, -i});
a[i] = {y1, y2};
}
SegT::build(1, 1, m);
sort(op.begin(), op.end());
for(int i = 1; i <= n; i++) {
col[i] = i;
pos[i].push_back(i);
exist[i] = 1;
}
for(Op &o : op) {
if(o.id > 0) insert(o.y1, o.y2, o.id);
else erase(o.y1, o.y2, -o.id);
}
for(int i = 1; i <= n; i++) ans += exist[i];
cout << ans << '\n';
return 0;
}