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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXN 50005

int n,q,S,T,top = 1,dep[MAXN],to[MAXM],wei[MAXM],nxt[MAXM],cur[MAXN],head[MAXN];
void Add_Edge (int u,int v,int w){
	to[++ top] = v,wei[top] = w,nxt[top] = head[u],head[u] = top;
	to[++ top] = u,wei[top] = 0,nxt[top] = head[v],head[v] = top;
}
bool BFS (){
	for (Int i = 1;i <= T;++ i) cur[i] = head[i],dep[i] = INF;
	queue <int> q;while (!q.empty()) q.pop ();dep[S] = 0,q.push (S);
	while (!q.empty()){
		int u = q.front();q.pop ();
		for (Int i = head[u];i;i = nxt[i]){
			int v = to[i],w = wei[i];
			if (dep[v] == INF && w){
				dep[v] = dep[u] + 1;
				q.push (v);
			}
		}
	}
	return dep[T] != INF;
}
int dfs (int u,int lim){
	if (u == T) return lim;
	int used = 0;
	for (Int i = cur[u];i && lim;i = nxt[i]){
		cur[u] = i;int v = to[i],w = wei[i];
		if (dep[v] == dep[u] + 1 && w){
			int del = dfs (v,min (lim,w));
			used += del,lim -= del,wei[i] -= del,wei[i ^ 1] += del;
		}
	}
	if (used == 0) dep[u] = INF;
	return used;
}
int Dinic (){
	int res = 0;
	while (BFS ()) res += dfs (S,INF);
	return res;
}
int sizt;
int id (int l,int r,int type){return type * sizt + ((l + r) | (l != r));}
void build (int l,int r,int type){
	int rt = id (l,r,type);
	if (l == r){
		if (type == 0) Add_Edge (S,rt,1);
		if (type == 1) Add_Edge (rt,T,1);
		return ;
	}
	int mid = (l + r) >> 1;
	build (l,mid,type),build (mid + 1,r,type);
	if (type == 0) Add_Edge (id (l,mid,type),rt,INF),Add_Edge (id (mid + 1,r,type),rt,INF);
	else Add_Edge (rt,id (l,mid,type),INF),Add_Edge (rt,id (mid + 1,r,type),INF);
}

#define PII pair<int,int>
#define MP make_pair

void modify (int l,int r,int type,int tl,int tr,vector<PII> &s){
	if (l >= tl && r <= tr) return s.push_back (MP (id (l,r,type),r - l + 1));
	int mid = (l + r) >> 1;
	if (tl <= mid) modify (l,mid,type,tl,tr,s);
	if (tr > mid) modify (mid + 1,r,type,tl,tr,s);
}
void update (int tl,int tr,int ql,int qr){
	static vector <PII> A,B;A.clear(),B.clear();
	modify (1,n,0,tl,tr,A),modify (1,n,1,ql,qr,B);
	for (Int i = 0;i < A.size();++ i) for (Int j = 0;j < B.size();++ j) Add_Edge (A[i].first,B[j].first,A[i].second * B[j].second);
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int lef[MAXN];
void Scan (){//输入+扫描线
	read (n,q);int tot = 0;
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	sort (mat + 1,mat + tot + 1);
	sizt = id (n,n,0);
	S = sizt * 2 + 3,T = S + 1;
	build (1,n,0),build (1,n,1);
	for (Int i = 1;i <= tot;++ i){
		if (mat[i].type == 1) for (Int j = mat[i].r1;j <= mat[i].r2;++ j) lef[j] = mat[i].c;
		else{
			for (Int j = mat[i].r1,lasw = mat[i].c - 1,lash = 0;j <= mat[i].r2 + 1;++ j){
				if (j == mat[i].r2 + 1 || lef[j] ^ lasw){
					if (lasw + 1 < mat[i].c) update (lash,j - 1,lasw + 1,mat[i].c - 1);
					lasw = lef[j],lash = j;
				}
			}
		}
	}
}

signed main(){
	Scan ();
	write (Dinic ()),putchar ('\n');
	return 0;
}

#include <bits/stdc++.h>

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return !f ? x : -x;
}

namespace Network
{
    #define pii std::pair<int, int>
    #define mkp(a, b) std::make_pair(a, b)
    const int inf = 1073741823;
    static const int N = 100010, M = 6000010;
    int dis[N], pre[N], vis[N], flo[N], que[N * 10];
    int tot = 1, fir[N], nex[M], got[M], tak[M], cst[M];

    inline void clear()
    {
        tot = 1;
        memset(fir, 0, sizeof(fir));
    }

    inline void AddEdge(int u, int v, int t, int w = 0)
    {
    	// printf("%d %d %d %d\n", u, v, t, w);
        nex[++tot] = fir[u], fir[u] = tot, got[tot] = v, tak[tot] = t, cst[tot] = w;
        nex[++tot] = fir[v], fir[v] = tot, got[tot] = u, tak[tot] = 0, cst[tot] = -w;
    }

    inline int bfs(int s, int t)
    {
        for (int i = 1; i <= t; ++i) dis[i] = 0;
        int l = 1, r = 1; dis[que[1] = s] = 1;
        while (l <= r)
        {
            int u = que[l++];
            for (int i = fir[u]; i; i = nex[i])
            {
                if (dis[got[i]] || !tak[i]) continue;
                dis[got[i]] = dis[u] + 1, que[++r] = got[i];
            }
        }
        return dis[t];
    }

    inline int dfs(int u, int t, int flow)
    {
        int rest = flow;
        if (u == t) return flow;
        for (int i = fir[u]; i && rest; i = nex[i])
        {
            if (dis[u] + 1 != dis[got[i]] || !tak[i]) continue;
            int tmp = dfs(got[i], t, std::min(tak[i], rest));
            if (!tmp) dis[got[i]] = 0;
            tak[i] -= tmp, tak[i ^ 1] += tmp, rest -= tmp;
        }
        return flow - rest;
    }

    inline int Maxflow(int s, int t)
    {
        int flow = 0;
        while (bfs(s, t)) flow += dfs(s, t, inf);
        return flow;
    }
    #undef pii
    #undef mkp(a, b)
}
// using namespace Network;

// #define pii std::pair<int, int>
const int N = 100010;
const int inf = 1073741823;
struct pii {int fir, sec, thr;};
#define mkp(a, b, c) pii{a, b, c}
inline int operator < (pii a, pii b)
{
	return a.fir < b.fir || a.fir == b.fir && a.sec < b.sec || a.fir == b.fir && a.sec == b.sec && a.thr < b.thr;
}

struct Node {int l, r, u, d, f;};
std::vector<Node> vec[N];
struct Sqr {int x1, x2, y1, y2;};
std::vector<Sqr> Square;
int s, t, cnt = 0, t1[N], t2[N], a[N], b[N], tot1, tot2;

inline void build1(int k, int l, int r)
{
    t1[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t1[k], t1[k >> 1], inf);
    if (l != r) build1(k << 1, l, mid), build1(k << 1 | 1, mid + 1, r);
}

inline void build2(int k, int l, int r)
{
    t2[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t2[k >> 1], t2[k], inf);
    if (l != r) build2(k << 1, l, mid), build2(k << 1 | 1, mid + 1, r);
}

inline void build(int k, int l, int r)
{
    if (l == r)
    {
        Network::AddEdge(s, t1[k], 1);
        Network::AddEdge(t2[k], t, 1);
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
}

inline void Ask1(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return a[++tot1] = t1[k], void();
    int mid = (l + r) >> 1;
    Ask1(k << 1, l, mid ,ql, qr), Ask1(k << 1 | 1, mid + 1, r, ql, qr);
}

inline void Ask2(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return b[++tot2] = t2[k], void();
    int mid = (l + r) >> 1;
    Ask2(k << 1, l, mid, ql, qr), Ask2(k << 1 | 1, mid + 1, r, ql, qr);
}

int Min[N << 1], tag[N << 1];
inline void Build(int k, int l, int r, int n)
{
    Min[k] = n, tag[k] = -1; int mid = (l + r) >> 1;
    if (l != r) Build(k << 1, l, mid, n), Build(k << 1 | 1, mid + 1, r, n);
}

inline void upd(int k)
{
    Min[k] = std::min(Min[k << 1], Min[k << 1 | 1]);
}

inline void down(int k)
{
    if (tag[k] == -1) return;
    Min[k << 1] = Min[k << 1 | 1] = tag[k];
    tag[k << 1] = tag[k << 1 | 1] = tag[k];
    tag[k] = -1;
}

inline void Mod(int k, int l, int r, int ql, int qr, int dat)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return Min[k] = dat, tag[k] = dat, void();
    int mid = (l + r) >> 1; down(k);
    Mod(k << 1, l, mid, ql, qr, dat), Mod(k << 1 | 1, mid + 1, r, ql, qr, dat), upd(k);
}

inline int Ask(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return inf;
    if (ql <= l && r <= qr) return Min[k];
    int mid = (l + r) >> 1; down(k);
    return std::min(Ask(k << 1, l, mid, ql, qr), Ask(k << 1 | 1, mid + 1, r, ql, qr));
}

int main()
{
    int n = read(), q = read(), lup = n, rup = n, Up = n;
    for (int i = 1; i <= q; ++i)
    {
        int x1 = read(), y1 = read(), x2 = read(), y2 = read();
        vec[x1].push_back(Node{y1, y2, x1, x2, -1}), vec[x2 + 1].push_back(Node{y1, y2, x1, x2, 1});
        // a[++cnt] = Node{x1, y1, y2, -1}, a[++cnt] = Node{x2 + 1, y1, y2, 1};
    }
    std::set<pii> Set; Set.insert(mkp(0, 0, n)), Set.insert(mkp(n + 1, 0, n)), Build(1, 1, n, n);
    for (auto val: vec[n + 1]) Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
    for (int i = n; i >= 1; --i)
    {
        for (auto val: vec[i]) if (val.f < 0)
        {
        	// if (val.l == 1) lup = i - 1;
        	// if (val.r == n) rup = i - 1;
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l, 0, 0)); --it1;
            if ((*it1).fir + 1 <= val.l - 1)
            {
            	// int d = (*it1).fir ? std::min(val.d, (*it1).thr) : lup;
                int d = Ask(1, 1, n, (*it1).fir + 1, val.l - 1);
            	Square.push_back(Sqr{(*it1).fir + 1, val.l - 1, i, d});
                Mod(1, 1, n, (*it1).fir + 1, val.l - 1, d);
            	// if (d == lup) lup = i - 1;
			}
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r + 1, 0, 0));
            if ((*it2).sec == i) continue;
            if (val.r + 1 <= (*it2).fir - 1)
            {
            	// int d = (*it2).fir != n + 1 ? std::min(val.d, (*it2).thr) : rup;
                int d = Ask(1, 1, n, val.r + 1, (*it2).fir - 1);
            	Square.push_back(Sqr{val.r + 1, (*it2).fir - 1, i, d});
                Mod(1, 1, n, val.r + 1, (*it2).fir - 1, i - 1);
            	// if (d == rup) rup = i - 1;
			}
        }
        for (auto val: vec[i]) if (val.f > 0)
        {
            int flg = true;
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l + 1, 0, 0));
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r, 0, 0));
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false; else --it1;
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false;
            if (flg && (*it1).fir + 1 <= (*it2).fir - 1)
            {
                int d = Ask(1, 1, n, (*it1).fir + 1, (*it2).fir - 1);
                Square.push_back(Sqr{(*it1).fir + 1, (*it2).fir - 1, i, d});
                Mod(1, 1, n, (*it1).fir + 1, (*it2).fir - 1, i - 1);
                // if ((*it1).fir == 0 && (*it2).fir == n + 1) Square[Square.size() - 1].y2 = lup, lup = rup = i - 1;
            }
            Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
        }
        if (vec[i].size()) Up = i - 1;
        for (auto val: vec[i]) if (val.f < 0)
        {
        	Mod(1, 1, n, val.l, val.r, i - 1);
            Set.erase(mkp(val.l, val.u, val.d)), Set.erase(mkp(val.r, val.u, val.d));
		}
    }
    if (Up) Square.push_back(Sqr{1, n, 1, Up});
    // for (auto val: Square) printf("%d %d %d %d\n", val.x1, val.x2, val.y1, val.y2);
    build1(1, 1, n), build2(1, 1, n);
	s = cnt + 1, t = cnt + 2, build(1, 1, n);
    for (auto s: Square)
    {
        int l1 = s.x1, r1 = s.x2, l2 = s.y1, r2 = s.y2;
        tot1 = 0, Ask1(1, 1, n, l1, r1);
        tot2 = 0, Ask2(1, 1, n, l2, r2);
        for (int i = 1; i <= tot1; ++i)
            for (int j = 1; j <= tot2; ++j)
                Network::AddEdge(a[i], b[j], inf);
    }
    // std::cout << t << std::endl;
    std::cout << Network::Maxflow(s, t) << std::endl;
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int $=2e6;
int n,m,fir[$],la[$],to[$],ec=1,w[$],ans,pc,S,T,lst[$],r1,r0,lc[$],rc[$];
vector<int>l[$],r[$],o[$];
#define md (L+R>>1)
void link(int a,int b,int v){la[++ec]=fir[a];fir[a]=ec;to[ec]=b;w[ec]=v;}
void con(int a,int b,int v=$){link(a,b,v);link(b,a,0);}
void build(int&p0=r0,int&p1=r1,int L=1,int R=n){
	p0=++pc; p1=++pc; if(L==R)return con(L,p0),con(p1,L+n);
	build(lc[p0],lc[p1],L,md); build(rc[p0],rc[p1],md+1,R);
	con(lc[p0],p0); con(rc[p0],p0); con(p1,lc[p1]); con(p1,rc[p1]);
}
void ask(int l,int r,int P,int o,int p0=r0,int p1=r1,int L=1,int R=n){//cerr<<l<<' '<<r<<' '<<P<<' '<<o<<endl;
	if(l>r)return;
	if(l<=L&&R<=r)return o?con(P,p1):con(p0,P);
	if(l<=md)ask(l,r,P,o,lc[p0],lc[p1],L,md);
	if(r>md)ask(l,r,P,o,rc[p0],rc[p1],md+1,R);
}
int d[$],q[$];
bool bfs(){
	for(int i=1;i<=pc;++i)d[i]=$; d[S]=0; q[1]=S;
	for(int h=1,t=1,p;p=q[h],h<=t;++h)for(int i=fir[p],y;y=to[i];i=la[i])if(d[y]>d[p]+1&&w[i])d[q[++t]=y]=d[p]+1;
	return d[T]<$;
}
int dfs(int p,int f){
	if(p==T)return f; int r=f;
	for(int i=fir[p],y;(y=to[i])&&r;i=la[i])if(d[y]==d[p]+1&&w[i]){
		int x=dfs(y,min(r,w[i]));
		if(!x)d[y]=$;
		w[i]-=x;w[i^1]+=x;r-=x;
	}return f-r;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)lst[i]=1;
	#define pb push_back
	for(int i=1,u,d,L,R;i<=m;++i)scanf("%d%d%d%d",&u,&L,&d,&R),
		l[u].pb(L),r[u].pb(R),o[u].pb(1),
		l[d].pb(L),r[d].pb(R),o[d].pb(0);
	l[n+1].pb(1);r[n+1].pb(n);o[n+1].pb(1);
	pc=n+n; S=++pc; T=++pc; build();
	for(int i=1;i<=n;++i)con(S,i,1),con(i+n,T,1);
	for(int i=1;i<=n+1;++i)for(int j=0;j<l[i].size();++j){
		int O=o[i][j],L=l[i][j],R=r[i][j];
		if(O){
			int lp=L;
			for(int k=L;k<=R;++k)if(lst[k]!=lst[lp])pc++,ask(lp,k-1,pc,0),ask(lst[lp],i-1,pc,1),lp=k;
			pc++,ask(lp,R,pc,0),ask(lst[R],i-1,pc,1);
		}else for(int k=L;k<=R;++k)lst[k]=i+1;
	}
	while(bfs())ans+=dfs(S,$);
	printf("%d",ans);
}

#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<algorithm>

#define maxn 200005
#define maxm 2000005
#define INF 0x3f3f3f3f

using namespace std;

inline int getint()
{
	int num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

int n,m,S,T,tot;
int rt1,rt2;
int lc[maxn<<3],rc[maxn<<3];
int tp[maxn],fir[maxn],nxt[maxm],to[maxm],cap[maxm],cnt=1;
int h[maxn],a[maxn];
int ans;
struct node{
	int x,l,r,p;
	friend bool operator <(node x,node y)
	{return x.x==y.x?x.p>y.p:x.x<y.x;}
}q[maxn];

inline void newnode(int u,int v,int w)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt,cap[cnt]=w;}
inline void insert(int u,int v,int w)
{newnode(u,v,w),newnode(v,u,0);}
inline void build(int &i,int l,int r,int p)
{
	if(!i)i=++tot;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(lc[i],l,mid,p),build(rc[i],mid+1,r,p);
	if(p)insert(i,lc[i],INF),insert(i,rc[i],INF);
	else insert(lc[i],i,INF),insert(rc[i],i,INF);
}

inline void build2(int i,int l,int r,int p)
{
	if(l==r){p?insert(i,T,1):insert(S,i,1);return;}
	int mid=(l+r)>>1;
	build2(lc[i],l,mid,p),build2(rc[i],mid+1,r,p);
}

inline void query(int i,int l,int r,int ql,int qr,int pos,int p)
{
	if(qr<l||r<ql)return;
	if(ql<=l&&r<=qr)
	{
		p?insert(pos,i,INF):insert(i,pos,INF);
		return;
	}
	int mid=(l+r)>>1;
	query(lc[i],l,mid,ql,qr,pos,p),query(rc[i],mid+1,r,ql,qr,pos,p);
}

inline void add(int x,int l,int r)
{
	int pos=-1,las=-1,y;
	for(int i=l;i<=r;i++)
	{
		y=a[i]+1,a[i]=-1;
		if(y!=las)
		{
			if(~pos)
			{
				int z=++tot;
				query(rt1,1,n,pos,i-1,z,0),query(rt2,1,n,las,x,z,1);
			}
			pos=i,las=y;
		}
	}
	if(~pos)
	{
		int z=++tot;
		query(rt1,1,n,pos,r,z,0),query(rt2,1,n,las,x,z,1);
	}
}
inline void update(int x,int l,int r){for(int i=l;i<=r;i++)a[i]=x;}

inline bool bfs()
{
	queue<int>Q;Q.push(S);
	memset(h,-1,sizeof h),h[S]=0;
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=fir[u];i;i=nxt[i])if(cap[i]&&!~h[to[i]])
		{
			h[to[i]]=h[u]+1;Q.push(to[i]);
			if(to[i]==T)return 1;
		}
	}
	return 0;
}
inline int dfs(int u,int flow)
{
	int used=0;
	if(u==T)return flow;
	for(int &i=tp[u];i;i=nxt[i])
		if(cap[i]&&h[to[i]]==h[u]+1)
		{
			int w=dfs(to[i],min(cap[i],flow-used));
			cap[i]-=w,cap[i^1]+=w,used+=w;
			if(flow==used)return flow;
		}
	if(!used)h[u]=-1;
	return used;
}
inline void dinic()
{while(bfs())memcpy(tp,fir,sizeof fir),ans+=dfs(S,INF);}

int main()
{
	n=getint(),m=getint();cnt=1;
	build(rt1,1,n,0),build(rt2,1,n,1);
	for(int i=1;i<=m;i++)
	{
		int X1=getint(),Y1=getint(),X2=getint(),Y2=getint();
		q[2*i-1]=(node){X1-1,Y1,Y2,0},q[2*i]=(node){X2,Y1,Y2,1};
	}
	sort(q+1,q+2*m+1);
	for(int i=1;i<=2*m;i++)
	{
		if(q[i].p)update(q[i].x,q[i].l,q[i].r);
		else add(q[i].x,q[i].l,q[i].r);
	}
	add(n,1,n);
	S=++tot,T=++tot;
	build2(rt1,1,n,0),build2(rt2,1,n,1);
	dinic();
	printf("%d\n",ans);
}

#include"bits/stdc++.h"
#define F(i,j,n) for(register int i=j;i<=n;i++)
#define D(i,j,n) for(register int i=j;i>=n;i--)
#define ll long long
#define N 40010
#define M 3500010
#define lc ch[x][0]
#define rc ch[x][1]
#define cl ch[y][0]
#define cr ch[y][1]
using namespace std;
namespace io{
	const int L=(1<<19)+1;
	char ibuf[L],*iS,*iT,c;int f;
	char gc(){
		if(iS==iT){
			iT=(iS=ibuf)+fread(ibuf,1,L,stdin);
			return iS==iT?EOF:*iS++;
		}
		return*iS++;
	}
	template<class I>void gi(I&x){
		for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;
		for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;
	}
};
using io::gi;
using io::gc;
struct no{
	int d,l,r;
	bool operator<(const no&b)const{return d<b.d;}
}a[N];
int head[M],nxt[M],to[M],w[M],f[M],d[M],tot=1,ch[M][2],tg[M],r[N],n,m,p,q,s,t,ans;
void adli(int u,int v,int l){
	++tot;nxt[tot]=head[u];head[u]=tot;to[tot]=v;w[tot]=l;
	++tot;nxt[tot]=head[v];head[v]=tot;to[tot]=u;w[tot]=0;
//	printf("%d %d %d\n",u,v,l);
}
int bfs(){
	int l=0,r=0,u,v,i;
	F(i,1,m)d[i]=0;
	f[r++]=s;d[s]=1;
	while(l<r){
		u=f[l++];
		for(i=head[u];i;i=nxt[i]){
			v=to[i];if(w[i]>0&&!d[v])d[v]=d[u]+1,f[r++]=v;
		}
	}
	return d[t];
}
int dfs(int u,int s){
	if(u==t)return s;
	int i,p,v,ans=0;
	for(i=head[u];i;i=nxt[i]){
		v=to[i];if(w[i]<=0||d[v]!=d[u]+1)continue;
		p=min(s-ans,w[i]);
		p=dfs(v,p);ans+=p;
		w[i]-=p;w[i^1]+=p;
		if(ans==s)return ans;
	}
	if(!ans)d[u]=-1;
	return ans;
}
void build(int &x,int l,int r){
	x=++m;
	if(l==r){adli(x,l,1);return;}
	int m=l+r>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	adli(x,lc,n);
	adli(x,rc,n);
}
void add(int &x,int y,int l,int r,int p,int q){
	x=++m;lc=cl;rc=cr;tg[x]=tg[y];
//	printf("%d %d %d %d %d %d\n",x,y,l,r,p,q);
	if(p<=l&&r<=q){
		tg[x]^=1;
		if(l!=r){
			if(!tg[lc])adli(x,lc,n);
			if(!tg[rc])adli(x,rc,n);
		}
		else if(!tg[x])adli(x,l,1);
		return;
	}
	int m=l+r>>1;
	if(p<=m)add(lc,cl,l,m,p,q);
	if(q>m)add(rc,cr,m+1,r,p,q);
	if(!tg[lc])adli(x,lc,n);
	if(!tg[rc])adli(x,rc,n);
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif
	gi(n);gi(q);m=n;s=++m;t=++m;
	F(i,1,n)adli(i,t,1);
	build(r[0],1,n);
	F(i,1,q){
		gi(a[i*2-1].d);gi(a[i*2-1].l);
		gi(a[i*2].d);gi(a[i*2-1].r);
		a[i*2].l=a[i*2-1].l;
		a[i*2].r=a[i*2-1].r;
		a[i*2].d++;
	}
//	puts("");
	q*=2;p=1;sort(a+1,a+q+1);
	F(i,1,n){
		r[i]=r[i-1];
		while(a[p].d==i){add(r[i],r[i],1,n,a[p].l,a[p].r);p++;}
		if(!tg[r[i]])adli(s,r[i],1);
//	while(bfs())ans+=dfs(s,n);printf("%d\n",ans);puts("");
	}
	while(bfs())ans+=dfs(s,n);
	printf("%d\n",ans);
	return 0;
}

#include <bits/stdc++.h>

typedef long long ll;
const int T = 1e5 + 50;
const int N = 4e6 + 50;
const int inf = 0x3f3f3f3f;

int n, m, s, t, q, rightest[N];
int head[T], nxt[N], ver[N], edge[N], d[T], cur[T], cnt = 1;

struct event
{
	int x, y1, y2, type;
	event(int x, int y1, int y2, int type) : x(x), y1(y1), y2(y2), type(type)
	{

	}
	inline bool operator<(const event& e)
	{
		return x != e.x ? x < e.x : type < e.type;
	}
};

struct rectangle
{
	int x1, y1, x2, y2;
	rectangle(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2)
	{

	}
};

int node_tot;

inline void add(int u, int v, int w)
{
	nxt[++cnt] = head[u], ver[cnt] = v, edge[cnt] = w, head[u] = cnt;
	nxt[++cnt] = head[v], ver[cnt] = u, edge[cnt] = 0, head[v] = cnt;
}

inline int lc(int o) { return o << 1; }
inline int rc(int o) { return o << 1 | 1; }

struct SegmentTree
{
	int id[N];
	std::vector<int> leaves, pool;
	void build(int o, int l, int r, bool d)
	{
		id[o] = ++node_tot;
		if (l != r)
		{
			const int mid = l + r >> 1;
			build(lc(o), l, mid, d);
			build(rc(o), mid + 1, r, d);
			if (d)
			{
				add(id[lc(o)], id[o], inf);
				add(id[rc(o)], id[o], inf);
			}
			else
			{
				add(id[o], id[lc(o)], inf);
				add(id[o], id[rc(o)], inf);
			}
		}
		else leaves.emplace_back(id[o]);
	}
	void search(int o, int l, int r, int ql, int qr)
	{
		if (ql <= l && r <= qr) pool.emplace_back(id[o]);
		else
		{
			const int mid = l + r >> 1;
			if (ql <= mid) search(lc(o), l, mid, ql, qr);
			if (qr > mid) search(rc(o), mid + 1, r, ql, qr);
		}
	}
	std::vector<int> extract(int l, int r)
	{
		pool.clear();
		search(1, 1, n, l, r);
		return pool;
	}
} T1, T2;

struct Segment2
{
	int tag[N], same[N];
	inline void maintain(int o, int v)
	{
		tag[o] = same[o] = v;
	}
	inline void push_up(int o)
	{
		int l = same[lc(o)], r = same[rc(o)];
		if (l == -1 || r == -1 || l != r) same[o] = -1;
		else same[o] = l;
	}
	inline void push_down(int o)
	{
		if (tag[o] != -1)
		{
			maintain(lc(o), tag[o]);
			maintain(rc(o), tag[o]);
			tag[o] = -1;
		}
	}
	void cover(int o, int l, int r, int ql, int qr, int v)
	{
		if (ql <= l && r <= qr) maintain(o, v);
		else
		{
			const int mid = l + r >> 1;
			push_down(o);
			if (ql <= mid) cover(lc(o), l, mid, ql, qr, v);
			if (qr > mid) cover(rc(o), mid + 1, r, ql, qr, v);
			push_up(o);
		}
	}
	void cover(int ql, int qr, int v)
	{
		cover(1, 1, n, ql, qr, v);
	}
	int find(int o, int l, int r, int p, int v)
	{
		push_down(o);
		if (same[o] == v) return inf;
		if (l == r) return l;
		const int mid = l + r >> 1;
		if (p <= mid)
		{
			int val = find(lc(o), l, mid, p, v);
			if (val != inf) return val;
		}
		return find(rc(o), mid + 1, r, p, v);
	}
	int query(int o, int l, int r, int p)
	{
		if (l == r) return same[o];
		else
		{
			push_down(o);
			const int mid = l + r >> 1;
			if (p <= mid) return query(lc(o), l, mid, p);
			else return query(rc(o), mid + 1, r, p);
		}
	}
} Tree;

inline bool dfs()
{
	std::queue<int> que;
	que.emplace(s);
	memset(d, 0, sizeof d); d[s] = 1;
	memcpy(cur, head, sizeof head);
	while (!que.empty())
	{
		int x = que.front(); que.pop();
		for (int i = head[x]; i; i = nxt[i])
		{
			int y = ver[i], w = edge[i];
			if (w && !d[y])
			{
				d[y] = d[x] + 1;
				if (y == t) return true;
				que.emplace(y);
			}
		}
	}
	return false;
}

inline int dinic(int x, int flow)
{
	if (x == t) return flow;
	int rest = flow;
	for (int& i = cur[x]; i; i = nxt[i])
		if (edge[i] && d[ver[i]] == d[x] + 1)
		{
			int y = ver[i];
			ll k = dinic(y, std::min(rest, edge[i]));
			if (!k) d[y] = 0;
			edge[i] -= k, edge[i ^ 1] += k, rest -= k;
			if (!rest) break;
		}
	return flow - rest;
}

inline int go()
{
	int flow = 0, maxflow = 0;
	while (dfs()) while (flow = dinic(s, 0x3f3f3f3f)) maxflow += flow;
	return maxflow;
}

inline char nc()
{
	static char buf[1000000], * p1 = buf, * p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? exit(0), EOF : *p1++;
}

inline int read()
{
	int res = 0;
	char ch;
	do ch = nc(); while (ch < 48 || ch > 57);
	do res = res * 10 + ch - 48, ch = nc(); while (ch >= 48 && ch <= 57);
	return res;
}

std::vector<event> eve;
std::vector<rectangle> rec;
inline void loadBannedRectangles()
{
	for (int i = 1; i <= q; ++i)
	{
		int x1 = read(), y1 = read(), x2 = read() + 1, y2 = read() + 1;
		eve.emplace_back(x1, y1, y2, 1);
		eve.emplace_back(x2, y1, y2, 0);
	}
	eve.emplace_back(n + 1, 1, n + 1, 1);
	std::sort(eve.begin(), eve.end());
}

inline int next_i(int i, int kk)
{
	if (i == kk) return inf;
	int vvv = Tree.find(1, 1, n, i, Tree.query(1, 1, n, i));
	if (vvv >= kk) return kk;
	return vvv;
}

inline void getFreeRectangles()
{
	Tree.cover(1, n, 1);
	for (auto &e : eve)
	{
		int x = e.x, y1 = e.y1, y2 = e.y2, t = e.type;
		if (t == 0)
		{
			Tree.cover(y1, y2 - 1, x);
		}
		else
		{
			for (int i = next_i(y1, y2), last = y1; i <= y2; i = next_i(i, y2))
			{
				int w = Tree.query(1, 1, n, i - 1);
				if (w < x)
				{
					rec.emplace_back(w, last, x - 1, i - 1);
				}
				last = i;
			}
		}
	}
}

int main()
{
	n = read(), q = read();
	loadBannedRectangles();
	getFreeRectangles();
	n += 2;
	T1.build(1, 1, n, true); T2.build(1, 1, n, false);
	s = ++node_tot, t = ++node_tot;

	for (int x : T1.leaves) add(s, x, 1);
	for (int x : T2.leaves) add(x, t, 1);

	for (auto &r :rec)
	{
		std::vector<int> v1 = T1.extract(r.x1, r.x2), v2 = T2.extract(r.y1, r.y2);
		for (int x : v1)
			for (int y: v2)
				add(x, y, inf);
	}
	printf("%d", go());
	return 0;
}

//minamoto
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e6+5,M=5e6+5,inf=0x3f3f3f3f;
struct eg{int v,nx,w;}e[M];int head[N],tot=1;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int cur[N],dep[N],q[N],S,T;
inline int min(R int x,R int y){return x<y?x:y;}
bool bfs(){
	memset(dep,-1,(T-S+1)<<2);
	memcpy(cur,head,(T-S+1)<<2);
	R int h,t,u;q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		u=q[h++];
		go(u)if(e[i].w&&dep[v]==-1)dep[v]=dep[u]+1,q[++t]=v;
	}
	return ~dep[T];
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int flow=0,fl;
	gg(u)if(dep[v]==dep[u]+1&&(fl=dfs(v,min(lim,e[i].w)))){
		flow+=fl,lim-=fl,e[i].w-=fl,e[i^1].w+=fl;
		if(!lim)break;
	}
	if(!flow)dep[u]=-1;
	return flow;
}
inline int dinic(){R int r=0;while(bfs())r+=dfs(S,inf);return r;}
int ls[N],rs[N],sl[N],sr[N],nd,lnd,rt1,rt2;
int n,Q;
void bd1(int &p,int l,int r){
	p=++nd;if(l==r)return add(S,p,1),void();
	int mid=(l+r)>>1;
	bd1(ls[p],l,mid),bd1(rs[p],mid+1,r);
	add(ls[p],p,inf),add(rs[p],p,inf);
}
void bd2(int &p,int l,int r){
	p=++nd;if(l==r)return add(p,T,1),void();
	int mid=(l+r)>>1;
	bd2(sl[p],l,mid),bd2(sr[p],mid+1,r);
	add(p,sl[p],inf),add(p,sr[p],inf);
}
void upd(int p,int l,int r,int o,int ql,int qr){
	if(ql<=l&&qr>=r)return add(o,p,inf),void();
	int mid=(l+r)>>1;
	if(ql<=mid)upd(sl[p],l,mid,o,ql,qr);
	if(qr>mid)upd(sr[p],mid+1,r,o,ql,qr);
}
void update(int p,int l,int r,int dl,int dr,int ql,int qr){
	if(dl<=l&&dr>=r)return upd(rt2,1,n,p,ql,qr);
	int mid=(l+r)>>1;
	if(dl<=mid)update(ls[p],l,mid,dl,dr,ql,qr);
	if(dr>mid)update(rs[p],mid+1,r,dl,dr,ql,qr);
}
struct node{
	int l,r,d;
	inline node(){}
	inline node(R int li):l(li){}
	inline node(R int li,R int ri,R int di):l(li),r(ri),d(di){}
	inline bool operator <(const node &b)const{return l<b.l;}
};
set<node>s;vector<node>L[10005],D[10005];
typedef set<node>::iterator IT;
inline IT spl(R int pos){
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)return it;
	--it;R int l=it->l,r=it->r,d=it->d;
	if(r<pos)return ++it;
	s.erase(it),s.insert(node(l,pos-1,d));
	return s.insert(node(pos,r,d)).fi;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&Q),S=0;
	bd1(rt1,1,n),T=nd<<1|1,bd2(rt2,1,n);
	for(R int i=1,l,r,u,d;i<=Q;++i){
		scanf("%d%d%d%d",&u,&l,&d,&r);
		L[u].pb(node(l,r,-1));
		D[d+1].pb(node(l,r,1));
	}
	L[n+1].pb(node(1,n,-1));
	s.insert(node(0,n+1,1));
	fp(i,1,n+1){
		for(auto v:D[i])s.insert(node(v.l,v.r,i));
		for(auto v:L[i]){
			IT itr=spl(v.r+1),itl=spl(v.l);
//			printf("%d\n",i);
//			printf("%d %d\n",v.l,v.r);
			for(IT it=itl;it!=itr;++it)
				if(it->d<i){
//					printf("%d %d %d %d\n",it->d,it->l,i-1,it->r);
					update(rt1,1,n,it->l,it->r,it->d,i-1);
				}
			s.erase(itl,itr);
		}
	}
	printf("%d\n",dinic());
	return 0;
}

#include <cstdio>
#include <algorithm>

const int INF = 0x3f3f3f3f;
const int MAXN = 1e4 + 5, MAXV = MAXN * 8, MAXE = MAXV * 10;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

struct edge
{
	int to, nxt, c;
}Graph[MAXE << 2];

int q[MAXV];
int head[MAXV], dep[MAXV], cur[MAXV];

int id1[MAXV], id2[MAXV];

int X[MAXN * 3], Y[MAXN * 3];
int lx[MAXN], rx[MAXN], ly[MAXN], ry[MAXN];
int sx[MAXN], sy[MAXN], tx[MAXN], ty[MAXN];

int sta, tar;
int N, Q, tot, cnt = 1;

bool ban[MAXN][MAXN];

void addEdge( const int from, const int to, const int C )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from], Graph[cnt].c = C;
	head[from] = cnt;
}

void addE( const int from, const int to, const int C )
{
	addEdge( from, to, C ), addEdge( to, from, 0 );
}

bool BFS( const int S, const int T )
{
	for( int i = 1 ; i <= tot ; i ++ ) dep[i] = INF;
	int h = 1, t = 0, u, v; dep[q[++ t] = S] = 0;
	while( h <= t )
	{
		u = q[h ++];
		for( int i = head[u] ; i ; i = Graph[i].nxt )
			if( Graph[i].c && dep[v = Graph[i].to] == INF )
				dep[q[++ t] = v] = dep[u] + 1;
	}
	return dep[T] < INF;
}

int DFS( const int u, const int T, const int lin )
{
	if( u == T ) return lin;
	int ret, used = 0, v, c;
	for( int &i = cur[u] ; i ; i = Graph[i].nxt )
	{
		v = Graph[i].to, c = Graph[i].c;
		if( c && dep[v] == dep[u] + 1 && ( ret = DFS( v, T, MIN( lin - used, c ) ) ) )
		{
			used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret;
			if( used == lin ) break;
		}
	}
	if( used < lin ) dep[u] = INF;
	return used;
}

int Dinic( const int S, const int T )
{
	int f = 0;
	while( BFS( S, T ) )
	{
		for( int i = 1 ; i <= tot ; i ++ ) cur[i] = head[i];
		f += DFS( S, T, INF );
	}
	return f;
}

bool build( const int x, const int l, const int r )
{
	if( l > r ) return false;
	id1[x] = ++ tot, id2[x] = ++ tot;
	if( l == r )
	{
		addE( sta, id1[x], 1 );
		addE( id2[x], tar, 1 );
		return true;
	}
	int mid = l + r >> 1;
	if( build( x << 1, l, mid ) )
		addE( id1[x << 1], id1[x], INF ),
		addE( id2[x], id2[x << 1], INF );
	if( build( x << 1 | 1, mid + 1, r ) )
		addE( id1[x << 1 | 1], id1[x], INF ),
		addE( id2[x], id2[x << 1 | 1], INF );
	return true;
}

void update( const int x, const int l, const int r, const int segL, const int segR, const int fr )
{
	if( segL <= l && r <= segR ) { addE( fr, id2[x], INF ); return ; }
	int mid = l + r >> 1;
	if( segL <= mid ) update( x << 1, l, mid, segL, segR, fr );
	if( mid < segR ) update( x << 1 | 1, mid + 1, r, segL, segR, fr );
}

void update( const int x, const int l, const int r, const int segL, const int segR, const int limL, const int limR )
{
	if( segL <= l && r <= segR ) { update( 1, 1, N, limL, limR, id1[x] ); return ; }
	int mid = l + r >> 1;
	if( segL <= mid ) update( x << 1, l, mid, segL, segR, limL, limR );
	if( mid < segR ) update( x << 1 | 1, mid + 1, r, segL, segR, limL, limR );
}

int main()
{
	read( N ), read( Q );
	int totx = 0, toty = 0;
	for( int i = 1 ; i <= Q ; i ++ )
	{
		read( sx[i] ), read( sy[i] ), read( tx[i] ), read( ty[i] );
		sx[i] --, sy[i] --;
		X[++ totx] = sx[i], X[++ totx] = tx[i];
		Y[++ toty] = sy[i], Y[++ toty] = ty[i];
	}

	X[++ totx] = 0, X[++ totx] = N;
	Y[++ toty] = 0, Y[++ toty] = N;
	std :: sort( X + 1, X + 1 + totx );
	std :: sort( Y + 1, Y + 1 + toty );
	totx = std :: unique( X + 1, X + 1 + totx ) - X - 1;
	toty = std :: unique( Y + 1, Y + 1 + toty ) - Y - 1;
	for( int i = 1 ; i < totx ; i ++ ) lx[i] = X[i] + 1, rx[i] = X[i + 1];
	for( int i = 1 ; i < toty ; i ++ ) ly[i] = Y[i] + 1, ry[i] = Y[i + 1];
	for( int i = 1 ; i <= Q ; i ++ )
	{
		sx[i] = std :: lower_bound( X + 1, X + 1 + totx, sx[i] ) - X;
		sy[i] = std :: lower_bound( Y + 1, Y + 1 + toty, sy[i] ) - Y;
		tx[i] = std :: lower_bound( X + 1, X + 1 + totx, tx[i] ) - X - 1;
		ty[i] = std :: lower_bound( Y + 1, Y + 1 + toty, ty[i] ) - Y - 1;
		for( int j = sx[i] ; j <= tx[i] ; j ++ )
			for( int k = sy[i] ; k <= ty[i] ; k ++ )
				ban[j][k] = true;
	}

	sta = ++ tot, tar = ++ tot;
	build( 1, 1, N );
	for( int i = 1, limX ; i < totx ; i ++ )
		for( int j = 1, limY ; j < toty ; j ++ )
			if( ! ban[i][j] )
			{
				for( limY = j ; limY < toty && ! ban[i][limY] ; limY ++ );
				limY --;
				for( limX = i ; limX < totx ; limX ++ )
				{
					bool flg = false;
					for( int k = j ; k <= limY ; k ++ )
						if( ban[limX][k] ) { flg = true; break; }
					if( flg ) break;
					for( int k = j ; k <= limY ; k ++ )
						ban[limX][k] = true;
				}
				limX --;
				update( 1, 1, N, lx[i], rx[limX], ly[j], ry[limY] );
			}

	write( Dinic( sta, tar ) ), putchar( '\n' );
	return 0;
}

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <cstdio>
#include <stdlib.h>
#include <stack>
#include <numeric>
#include <bitset>
#include <cctype>
#include <list>
#include <assert.h>
#include <random>
#include <unordered_map>
#include <ctime>
typedef  long long ll;
using namespace std;
#define INF 2147483647
#define infL (1LL<<60)
#define inf (1<<30)
#define inf9 (1000000000)
#define MOD 1000000007
#define EPS 1e-9
#define Gr 9.8
#define PI acos(-1)
#define REP(i,n) for(int (i)=0;(i)<(int)(n);(i)++)
#define REQ(i,n) for(int (i)=1;(i)<=(int)(n);(i)++)
#define lch (rt<<1)
#define rch (rt<<1|1)
#define readmp(n) for(int i=0,u,v;i<n;i++) {scanf("%d%d",&u,&v); mp[u].push_back(v); mp[v].push_back(u);}
typedef pair<int, int> pii;
typedef long double ld;
typedef unsigned int uint;
#define MAX 10005
bool debug = 0;
int n,m,k;
int dx[4] = {0,1,0,-1}, dy[4] = {1,0,-1,0};
string direc="RDLU";
ll ln, lk, lm;
void etp(bool f = 0) {
    puts(f ?"YES" : "NO");
    exit(0);
}
void addmod(int &x, int y, int mod = MOD) {
    x+=y; if (x>=mod) x-=mod;
    assert(x>=0 && x<mod);
}
void et() {
    puts("-1"); exit(0);
}

#define F int
#define C int
#define F_INF (1<<29)
#define C_INF (1<<29)
#define MAXE 10000000
#define MAXV 100000
class MaxFlow{
public:
    F cap[MAXE],flow[MAXE];
    int to[MAXE],prev[MAXE],last[MAXV],used[MAXV],level[MAXV];
    int V,E;

    MaxFlow(int n){
        V = n; E = 0;
        REP(i,V) last[i] = -1;
    }

    void add_edge(int x, int y, F f){
//        printf("%d %d %d\n",x,y,f);
        cap[E] = f; flow[E] = 0; to[E] = y; prev[E] = last[x]; last[x] = E; E++;
        cap[E] = 0; flow[E] = 0; to[E] = x; prev[E] = last[y]; last[y] = E; E++;
    }

    bool bfs(int s, int t){
        int i;
        REP(i,V) level[i] = -1;
        queue <int> q;
        q.push(s); level[s] = 0;
        while(!q.empty()){
            int x = q.front(); q.pop();
            for(i=last[x];i>=0;i=prev[i]) if(level[to[i]] == -1 && cap[i] > flow[i]) {q.push(to[i]); level[to[i]] = level[x] + 1;}
        }
        return (level[t] != -1);
    }

    F dfs(int v, int t, F f){
        int i;
        if(v == t) return f;
        for(i=used[v];i>=0;used[v]=i=prev[i]) if(level[to[i]] > level[v] && cap[i] > flow[i]){
            F tmp = dfs(to[i],t,min(f,cap[i]-flow[i]));
            if(tmp > 0) {flow[i] += tmp; flow[i^1] -= tmp; return tmp;}
        }
        return 0;
    }
    F maxflow(int s, int t){
        while(bfs(s,t)){
            REP(i,V) used[i] = last[i];
            while(dfs(s,t,F_INF) != 0);
        }
        F ans = 0;
        for(int i=last[s];i>=0;i=prev[i]) ans += flow[i];
        return ans;
    }
};

struct rec {
    int x1,x2,y1,y2;
    void rd() {
        scanf("%d%d%d%d", &x1,&y1,&x2,&y2);
    }
    bool operator < (const rec &o) const {
        if (x1==o.x1) return y1<o.y1;
        return x1<o.x1;
    }
    void pp() {
        printf("%d %d - %d %d\n", x1,y1,x2,y2);
    }
}q[MAX];
vector<rec> vs;
void addrec(int a, int A, int b, int B) {
    if (A<a || B<b) return;
    vs.push_back({a,A,b,B});
}
void init() {
    set<pair<pii, int>> sp;
    sp.insert({{1,n}, 0});
    REQ(i, m) {
        int u=q[i].y2, d=q[i].y1;
        auto it = sp.upper_bound({{d,n+1}, n+1});
        it--;
        vector<pair<pii, int>> vp;
        while(it != sp.end()) {
            auto pp = (*it);
            if (pp.first.first > u) break;
            if (pp.first.first>=d && pp.first.second<=u) {
                addrec(pp.second+1, q[i].x1-1, pp.first.first, pp.first.second
                );
                auto tmp=it; it++;
                sp.erase(tmp);
            } else if (pp.first.first<d && pp.first.second<=u) {
                addrec(pp.second+1, q[i].x1-1, d, pp.first.second);
                auto tmp=it; it++;
                vp.push_back({{pp.first.first, d-1}, pp.second});
                sp.erase(tmp);
            } else if (pp.first.first>=d && pp.first.second>u) {
                addrec(pp.second+1, q[i].x1-1, pp.first.first, u);
                auto tmp=it; it++;
                vp.push_back({{u+1, pp.first.second}, pp.second});
                sp.erase(tmp);
            } else if (pp.first.first<d && pp.first.second>u) {
                addrec(pp.second+1, q[i].x1-1, d, u);
                auto tmp=it; it++; sp.erase(tmp);

                vp.push_back({{pp.first.first, d-1}, pp.second});
                vp.push_back({{u+1, pp.first.second}, pp.second});
            }
        }
        vp.push_back({{d,u}, q[i].x2});
        for(auto zz : vp) sp.insert(zz);
    }
//    for(auto z : vs) z.pp();
}
void fmain(int tid) {
    scanf("%d", &n);
    scanf("%d", &m);
    REQ(i,m) q[i].rd();
    q[m+1] = {n+1,n+1,1,n}; m++;
    sort(1+q,q+1+m);
    init();

    int N = vs.size(),M;
    for(M=1;n+2>M;M<<=1);
    MaxFlow mf = MaxFlow(4*M-2 + N + 5);
    int T = 4*M-2+N+1;
    int P=M+M-1;
    for(int i=1;i<M+M;i++) {
        if (i!=1) {
            mf.add_edge(i, i>>1, inf9);
            mf.add_edge((i>>1) +P , i+P, inf9);
        }
    }
    REQ(i,n) {
        mf.add_edge(0, i+M, 1);
        mf.add_edge(i+M + P, T, 1);
    }
    REP(i,N){
        int id = 4*M-2 + i+1;
        for(int l=vs[i].x1+M-1, r=vs[i].x2+M+1;l^r^1;l>>=1,r>>=1) {
            if (~l&1) mf.add_edge(l^1, id, inf9);
            if (r&1) mf.add_edge(r^1, id, inf9);
        }
        for(int l=vs[i].y1+M-1, r=vs[i].y2+M+1;l^r^1;l>>=1,r>>=1) {
            if (~l&1) mf.add_edge(id, (l^1)+P, inf9);
            if (r&1) mf.add_edge(id, (r^1)+P, inf9);
        }
    }
    printf("%d\n", mf.maxflow(0, T));
}
int main() {
    int t=1;
//    scanf("%d", &t);
    REQ(i,t) {
        fmain(i);
    }
    return 0;
}

#include <bits/stdc++.h>
#define N 10005
#define inf 666666666
using namespace std;
const int V=N*30,E=N*1000;
inline int read()
{int x=0;
char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
struct node
{int xa,xb,ya,yb;
}t[N];
inline bool cmp(node a,node b)
{return a.xa<b.xa;}
struct tupl
{int l,r,x;
}del[V],ins[V];
int tp1,tp2;
struct comp
{bool operator () (const tupl &a,const tupl &b) const
{return a.l<b.l;}
};
set <tupl,comp> st;
set <tupl,comp>::iterator it1,it2;
int head[V],nxt[E],v[E],w[E],n,m,tot=1;
int fr,ed,lev[V],cur[V],cnt=0,ans=0,rl,rr;
int lc[V],rc[V],cnt1=0,cnt2=0;
int lp[51],rp[51];
inline void add(int a,int b,int c)
{tot++;nxt[tot]=head[a];head[a]=tot;v[tot]=b;w[tot]=c;
tot++;nxt[tot]=head[b];head[b]=tot;v[tot]=a;w[tot]=0;
}
inline tupl make(int x,int l,int r)
{tupl tp;tp.x=x;tp.l=l;tp.r=r;return tp;}
void build1(int pos,int l,int r)
{if (l==r) {add(fr,pos,1);return;}
int mid=(l+r)>>1;
lc[pos]=++cnt;
rc[pos]=++cnt;
add(lc[pos],pos,inf);
add(rc[pos],pos,inf);
build1(lc[pos],l,mid);
build1(rc[pos],mid+1,r);
}
void build2(int pos,int l,int r)
{if (l==r) {add(pos,ed,1);return;}
int mid=(l+r)>>1;
lc[pos]=++cnt;
rc[pos]=++cnt;
add(pos,lc[pos],inf);
add(pos,rc[pos],inf);
build2(lc[pos],l,mid);
build2(rc[pos],mid+1,r);
}
void get1(int pos,int l,int r,int lt,int rt)
{if (l==lt&&r==rt) {lp[++cnt1]=pos;return;}
int mid=(l+r)>>1;
if (rt<=mid) {get1(lc[pos],l,mid,lt,rt);return;}
if (lt>mid) {get1(rc[pos],mid+1,r,lt,rt);return;}
get1(lc[pos],l,mid,lt,mid);
get1(rc[pos],mid+1,r,mid+1,rt);
}
void get2(int pos,int l,int r,int lt,int rt)
{if (l==lt&&r==rt) {rp[++cnt2]=pos;return;}
int mid=(l+r)>>1;
if (rt<=mid) {get2(lc[pos],l,mid,lt,rt);return;}
if (lt>mid) {get2(rc[pos],mid+1,r,lt,rt);return;}
get2(lc[pos],l,mid,lt,mid);
get2(rc[pos],mid+1,r,mid+1,rt);
}
inline void addr(int xa,int xb,int ya,int yb)
{if (xa>xb||ya>yb) return;
cnt1=cnt2=0;cnt++;
get1(rl,1,n,xa,xb);
get2(rr,1,n,ya,yb);
for (int i=1;i<=cnt1;i++) add(lp[i],cnt,inf);
for (int i=1;i<=cnt2;i++) add(cnt,rp[i],inf);
}
inline void build()
{int i,j;
rl=++cnt;build1(rl,1,n);
rr=++cnt;build2(rr,1,n);
st.insert(make(1,1,n));
for (i=1;i<=m;i++)
{it1=st.lower_bound(make(0,t[i].ya,0));
it2=st.lower_bound(make(0,t[i].yb+1,0));
tp1=tp2=0;
if (it1!=st.begin()) it1--;
while (it1!=it2)
{if ((*it1).r<t[i].ya||(*it1).l>t[i].yb) {it1++;continue;}
del[++tp1]=(*it1);
if ((*it1).l<t[i].ya&&(*it1).r>t[i].yb)
{ins[++tp2]=make((*it1).x,(*it1).l,t[i].ya-1);
ins[++tp2]=make((*it1).x,t[i].yb+1,(*it1).r);
addr((*it1).x,t[i].xa-1,t[i].ya,t[i].yb);
it1++;continue;
}
if ((*it1).l<t[i].ya)
{ins[++tp2]=make((*it1).x,(*it1).l,t[i].ya-1);
addr((*it1).x,t[i].xa-1,t[i].ya,(*it1).r);
it1++;continue;
}
if ((*it1).r>t[i].yb)
{ins[++tp2]=make((*it1).x,t[i].yb+1,(*it1).r);
addr((*it1).x,t[i].xa-1,(*it1).l,t[i].yb);
it1++;continue;
}
addr((*it1).x,t[i].xa-1,(*it1).l,(*it1).r);
it1++;
}
ins[++tp2]=make(t[i].xb+1,t[i].ya,t[i].yb);
for (j=1;j<=tp1;j++) st.erase(st.lower_bound(del[j]));
for (j=1;j<=tp2;j++) st.insert(ins[j]);
}
for (it1=st.begin();it1!=st.end();it1++)
{addr((*it1).x,n,(*it1).l,(*it1).r);}
}
int q[V],l=1,r=0;
inline int min(int a,int b)
{return a<b?a:b;}
inline bool bfs()
{for (int i=1;i<=cnt;i++) lev[i]=inf;
l=1;r=0;q[++r]=fr;lev[fr]=0;
while (l<=r)
{int t=q[l++];
for (int i=head[t];i;i=nxt[i])
{if (w[i]&&lev[v[i]]>lev[t]+1)
{lev[v[i]]=lev[t]+1;
q[++r]=v[i];
}
}
}
return lev[ed]<inf;
}
int dfs(int pl,int flow)
{if (pl==ed) {return flow;}
int cost=0;
for (int i=cur[pl];i;i=nxt[i])
{cur[pl]=i;
if (w[i]>0&&lev[v[i]]==lev[pl]+1)
{int c=dfs(v[i],min(flow-cost,w[i]));
cost+=c;w[i]-=c;w[i^1]+=c;
if (cost==flow) return flow;
}
}
return cost;
}
inline void dinic()
{while (bfs())
{for (int i=1;i<=cnt;i++) cur[i]=head[i];
ans+=dfs(fr,inf);
}
return;
}
int main (){
	int i;
	n=read();m=read();
	fr=++cnt;ed=++cnt;
	for (i=1;i<=m;i++)
	{t[i].xa=read();t[i].ya=read();
	t[i].xb=read();t[i].yb=read();
	}
	sort(t+1,t+m+1,cmp);
	build();dinic();
	printf ("%d\n",ans);
	return 0;
}

#include <cstdio>
#include <cstring>
#include <algorithm>

int mx[2000001];
struct line{
	int x,l,r,ord;
}num[600001];
int n,Q;
bool cmp(line a,line b){
	return a.x<b.x||(a.x==b.x&&a.ord>b.ord);
}
int head[200001],nxt[4000001],b[4000001],v[4000001],k=1,S,T,cnt,edge[2][200001],time[200001],p[200001];
void push(int s,int t,int val){
	nxt[++k]=head[s];
	head[s]=k;
	b[k]=t;
	v[k]=val;
}
void link(int s,int t,int val=0x7f7f7f7f){
//	printf("%d %d\n",s,t);
	push(s,t,val);
	push(t,s,0);
}
void pushup(int root){
	if(time[root<<1]==time[root<<1|1])time[root]=time[root<<1];
	else time[root]=-1;
}
void build(int root,int l,int r){
	if(l==r){
		edge[0][root]=l;
		edge[1][root]=l+n;
		time[root]=1;
		return;
	}
	build(root<<1,l,(l+r)>>1);
	build(root<<1|1,((l+r)>>1)+1,r);
	edge[0][root]=++cnt;
	link(edge[0][root<<1],edge[0][root]);
	link(edge[0][root<<1|1],edge[0][root]);
	edge[1][root]=++cnt;
	link(edge[1][root],edge[1][root<<1]);
	link(edge[1][root],edge[1][root<<1|1]);
	pushup(root);
}
void addedge(int root,int l,int r,int el,int er,int x){
	if(el>r||er<l)return;
	if(el<=l&&er>=r)return link(x,edge[1][root]);
	addedge(root<<1,l,(l+r)>>1,el,er,x);
	addedge(root<<1|1,((l+r)>>1)+1,r,el,er,x);
}
void spread(int root){
	if(~time[root])time[root<<1]=time[root<<1|1]=time[root];
}
void del(int root,int l,int r,int el,int er,int x){
	if(el>r||er<l)return;
	if(el<=l&&er>=r&&~time[root]){
		addedge(1,1,n,time[root],x-1,edge[0][root]);
		time[root]=0x7f7f7f7f;
		return;
	}
	spread(root);
	del(root<<1,l,(l+r)>>1,el,er,x);
	del(root<<1|1,((l+r)>>1)+1,r,el,er,x);
	pushup(root);
}
void clear(int root,int l,int r,int el,int er,int x){
	if(el>r||er<l)return;
	if(el<=l&&er>=r){
		time[root]=x;
		return;
	}
	spread(root);
	clear(root<<1,l,(l+r)>>1,el,er,x);
	clear(root<<1|1,((l+r)>>1)+1,r,el,er,x);
	pushup(root);
}
int dis[2000001],q[2000001],h,t;
bool bfs(){
	for(int i=0;i<=cnt;i++)dis[i]=-1;
	h=t=0;
	q[++t]=S;
	dis[S]=0;
	while(h<t){
		++h;
		for(int i=head[q[h]];i;i=nxt[i])
			if(v[i]&&!~dis[b[i]]){
				dis[b[i]]=dis[q[h]]+1;
				q[++t]=b[i];
				if(b[i]==T)return 1;
			}
	}
	return 0;
}
int dfs(int x,int flow){
	if(x==T||!flow)return flow;
	int used=0;
	for(int i=p[x];i;i=nxt[i])
		if(v[i]&&dis[b[i]]==dis[x]+1){
			int w=dfs(b[i],std::min(flow-used,v[i]));
			v[i]-=w;
			v[i^1]+=w;
			if(w)p[x]=i;
			used+=w;
			if(used==flow)return used;
		}
	if(!used)dis[x]=-1;
	return used;
}
void debug(int root,int l,int r){
	printf("debug %d %d %d %d\n",root,l,r,time[root]);
	if(l==r)return;
	spread(root);
	debug(root<<1,l,(l+r)>>1);
	debug(root<<1|1,((l+r)>>1)+1,r);
}
void debug(int x,int o){
//	if(x>n&&x<=(n<<1))printf("con %d %d\n",o,x-n);
	for(int i=head[x];i;i=nxt[i])
		if(v[i])debug(b[i],o);
}
int main(){
	scanf("%d%d",&n,&Q);
	T=cnt=(n<<1)+1;
	for(int i=1;i<=n;i++)link(S,i,1),link(i+n,T,1);
//	return 0;
	for(int i=1,x1,y1,x2,y2;i<=Q;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		num[(i<<1)-1]=(line){x1,y1,y2,1};
		num[(i<<1)  ]=(line){x2,y1,y2,-1};
	}
	std::sort(num+1,num+(Q<<1)+1,cmp);
	build(1,1,n);
	for(int i=1;i<=(Q<<1);i++){
		if(num[i].ord==1)del(1,1,n,num[i].l,num[i].r,num[i].x);
		else clear(1,1,n,num[i].l,num[i].r,num[i].x+1);
//		debug(1,1,n);
	}
	del(1,1,n,1,n,n+1);
//	for(int i=1;i<=n;i++)debug(i,i);
	int ans=0;
	while(bfs()){
		memcpy(p,head,sizeof p);
		ans+=dfs(S,0x7f7f7f7f);
//		for(int i=1;i<=k;i+=2)printf("%d %d %d\n",b[i],b[i^1],v[i]);
	}
	printf("%d\n",ans);
}

// powered by c++11
// by Isaunoya
#include <bits/stdc++.h>

#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define Rep(i, x, y) for (register int i = (x); i >= (y); --i)

using namespace std;
using db = double;
using ll = long long;
using uint = unsigned int;
using ull = unsigned long long;

#define pii pair<int, int>
#define fir first
#define sec second

template <class T>

void cmax(T& x, const T& y) {
  if (x < y) x = y;
}

template <class T>

void cmin(T& x, const T& y) {
  if (x > y) x = y;
}

#define all(v) v.begin(), v.end()
#define sz(v) ((int)v.size())
#define pb emplace_back

template <class T>

void sort(vector<T>& v) {
  sort(all(v));
}

template <class T>

void reverse(vector<T>& v) {
  reverse(all(v));
}

template <class T>

void unique(vector<T>& v) {
  sort(all(v)), v.erase(unique(all(v)), v.end());
}

void reverse(string& s) { reverse(s.begin(), s.end()); }

const int io_size = 1 << 23 | 233;
const int io_limit = 1 << 22;
struct io_in {
  char ch;
#ifndef __WIN64
  char getchar() {
    static char buf[io_size], *p1 = buf, *p2 = buf;

    return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, io_size, stdin), p1 == p2) ? EOF : *p1++;
  }
#endif
  io_in& operator>>(char& c) {
    for (c = getchar(); isspace(c); c = getchar())
      ;

    return *this;
  }
  io_in& operator>>(string& s) {
    for (s.clear(); isspace(ch = getchar());)
      ;

    if (!~ch) return *this;

    for (s = ch; !isspace(ch = getchar()) && ~ch; s += ch)
      ;

    return *this;
  }

  io_in& operator>>(char* str) {
    char* cur = str;
    while (*cur) *cur++ = 0;

    for (cur = str; isspace(ch = getchar());)
      ;
    if (!~ch) return *this;

    for (*cur = ch; !isspace(ch = getchar()) && ~ch; *++cur = ch)
      ;

    return *++cur = 0, *this;
  }

  template <class T>

  void read(T& x) {
    bool f = 0;
    while ((ch = getchar()) < 48 && ~ch) f ^= (ch == 45);

    x = ~ch ? (ch ^ 48) : 0;
    while ((ch = getchar()) > 47) x = x * 10 + (ch ^ 48);
    x = f ? -x : x;
  }

  io_in& operator>>(int& x) { return read(x), *this; }

  io_in& operator>>(ll& x) { return read(x), *this; }

  io_in& operator>>(uint& x) { return read(x), *this; }

  io_in& operator>>(ull& x) { return read(x), *this; }

  io_in& operator>>(db& x) {
    read(x);
    bool f = x < 0;
    x = f ? -x : x;
    if (ch ^ '.') return *this;

    double d = 0.1;
    while ((ch = getchar()) > 47) x += d * (ch ^ 48), d *= .1;
    return x = f ? -x : x, *this;
  }
} in;

struct io_out {
  char buf[io_size], *s = buf;
  int pw[233], st[233];

  io_out() {
    set(7);
    rep(i, pw[0] = 1, 9) pw[i] = pw[i - 1] * 10;
  }

  ~io_out() { flush(); }

  void io_chk() {
    if (s - buf > io_limit) flush();
  }

  void flush() { fwrite(buf, 1, s - buf, stdout), fflush(stdout), s = buf; }

  io_out& operator<<(char c) { return *s++ = c, *this; }

  io_out& operator<<(string str) {
    for (char c : str) *s++ = c;
    return io_chk(), *this;
  }

  io_out& operator<<(char* str) {
    char* cur = str;
    while (*cur) *s++ = *cur++;
    return io_chk(), *this;
  }

  template <class T>

  void write(T x) {
    if (x < 0) *s++ = '-', x = -x;

    do {
      st[++st[0]] = x % 10, x /= 10;
    } while (x);

    while (st[0]) *s++ = st[st[0]--] ^ 48;
  }

  io_out& operator<<(int x) { return write(x), io_chk(), *this; }

  io_out& operator<<(ll x) { return write(x), io_chk(), *this; }

  io_out& operator<<(uint x) { return write(x), io_chk(), *this; }

  io_out& operator<<(ull x) { return write(x), io_chk(), *this; }

  int len, lft, rig;

  void set(int _length) { len = _length; }

  io_out& operator<<(db x) {
    bool f = x < 0;
    x = f ? -x : x, lft = x, rig = 1. * (x - lft) * pw[len];
    return write(f ? -lft : lft), *s++ = '.', write(rig), io_chk(), *this;
  }
} out;

int n, m, s, t;
const int maxn = 3e4 + 43;
const int inf = 1e9;

namespace Dinic {
int head[maxn << 6], cur[maxn << 6];
struct List {
  int v, nxt, w;
} e[maxn << 7];
int cnt = 1;
void add(int u, int v, int w) {
  e[++cnt] = { v, head[u], w }, head[u] = cnt;
  e[++cnt] = { u, head[v], 0 }, head[v] = cnt;
}

int d[maxn << 6];
bool bfs(int s) {
  memset(d, 0, sizeof(d));
  queue<int> q;
  d[s] = 1, q.push(s);
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    for (int i = head[u]; i; i = e[i].nxt) {
      int& v = e[i].v;
      if (!d[v] && e[i].w) {
        d[v] = d[u] + 1;
        q.push(v);
      }
    }
  }
  return d[t];
}

int dfs(int u, int flow) {
  if (u == t) return flow;
  int fl = 0;
  for (int i = cur[u]; i; i = e[i].nxt) {
    int& v = e[i].v;
    cur[u] = i;
    if (d[v] == d[u] + 1 && e[i].w) {
      int dis = dfs(v, min(flow, e[i].w));
      if (dis) {
        e[i].w -= dis;
        e[i ^ 1].w += dis;
        flow -= dis;
        fl += dis;
        if (!flow) return fl;
      }
    }
  }
  return fl;
}

int dinic() {
  int ans = 0;
  while (bfs(s)) {
    memcpy(cur, head, sizeof(head));
    ans += dfs(s, inf);
  }
  return ans;
}
}  // namespace Dinic

using Dinic ::add;

namespace SegMentTree {
int id[maxn << 2];
int tag[maxn << 2];
int cnt;

void pushup(int p) {
  id[p] = ++cnt;
  if (!tag[p << 1]) add(id[p], id[p << 1], inf);
  if (!tag[p << 1 | 1]) add(id[p], id[p << 1 | 1], inf);
}

void build(int p, int l, int r) {
  if (l == r) {
    id[p] = (n + l);
    return;
  }
  int mid = l + r >> 1;
  build(p << 1, l, mid);
  build(p << 1 | 1, mid + 1, r);
  pushup(p);
}

void upd(int p, int a, int b, int l, int r, int v) {
  if (a <= l && r <= b) {
    tag[p] += v;
    return;
  }
  int mid = l + r >> 1;
  if (a <= mid) upd(p << 1, a, b, l, mid, v);
  if (b > mid) upd(p << 1 | 1, a, b, mid + 1, r, v);
  pushup(p);
}
}  // namespace SegMentTree

using SegMentTree ::build;
using SegMentTree ::upd;

struct chg {
	int l, r, val;
};
vector <chg> v[maxn];

signed main() {
  // code begin.
	in >> n >> m;
	s = 2 * n + 1, t = 2 * n + 2, SegMentTree :: cnt = 2 * n + 2;
	build(1, 1, n);
	while(m--) {
		int x1, y1, x2, y2;
		in >> x1 >> y1 >> x2 >> y2;
		v[x1].push_back({y1, y2, 1});
		v[x2 + 1].push_back({y1, y2, -1});
	}
	rep(i , 1 , n) add(s, i, 1), add(i + n, t, 1);
	rep(i , 1 , n) {
		for(auto x : v[i]) {
			int l = x.l, r = x.r, v = x.val;
			upd(1, l, r, 1, n, v);
		}
		if(! SegMentTree :: tag[1])
			add(i, SegMentTree :: id[1] , inf);
	}
	int ans = Dinic :: dinic();
	out << ans << '\n';
  return 0;
  // code end.
}

#include <bits/stdc++.h>
#define PB push_back
using namespace std;
typedef long long ll;

struct Dinic{
  static const int MXN = (int)2e6;
  struct Edge{ int v,f,re; };
  int n,s,t,level[MXN];
  vector<Edge> E[MXN];
  void init(int _n, int _s, int _t){
    n = _n; s = _s; t = _t;
//    for (int i=0; i<n; i++) E[i].clear();
  }
  void add_edge(int u, int v, int f){
    E[u].PB({v,f,(int)E[v].size()});
    E[v].PB({u,0,(int)E[u].size()-1});
  }
  bool BFS(){
    for (int i=0; i<n; i++) level[i] = -1;
    queue<int> que;
    que.push(s);
    level[s] = 0;
    while (!que.empty()){
      int u = que.front(); que.pop();
      for (auto &it : E[u]){
        if (it.f > 0 && level[it.v] == -1){
          level[it.v] = level[u]+1;
          que.push(it.v);
        }
      }
    }
    return level[t] != -1;
  }
  int DFS(int u, int nf){
    if (u == t) return nf;
    int res = 0;
    for (auto &it : E[u]){
      if (it.f > 0 && level[it.v] == level[u]+1){
        int tf = DFS(it.v, min(nf,it.f));
        res += tf; nf -= tf; it.f -= tf;
        E[it.v][it.re].f += tf;
        if (nf == 0) return res;
      }
    }
    if (!res) level[u] = -1;
    return res;
  }
  int flow(int res=0){
    while ( BFS() )
      res += DFS(s,2147483647);
    return res;
  }
}flow;

struct Node {
  int l, r, y, v;

  bool operator<(const Node& rhs) const {
    return y < rhs.y;
  }
};

int n, q, s, t, tot;
vector<Node> seg;

namespace segtree {
  static const int N = (int)1e4 + 5;
  static const int inf = (int)1e9;
  int id[N * 4], id2[N * 4], tag[N * 4];
  void pull(int o, int lson, int rson) {
    id[o] = tot++;
    if(!tag[lson]) flow.add_edge(id[o], id[lson], inf);
    if(!tag[rson]) flow.add_edge(id[o], id[rson], inf);
  }
  void build1(int l, int r, int o=0) {
    if(l + 1 == r) return void(id[o] = n + l);
    int mid = (l + r) >> 1, lson = o * 2 + 1, rson = o * 2 + 2;
    build1(l, mid, lson);
    build1(mid, r, rson);
    pull(o, lson, rson);
  }
  void build2(int l, int r, int o=0) {
    if(l + 1 == r) return void(id2[o] = l);
    int mid = (l + r) >> 1, lson = o * 2 + 1, rson = o * 2 + 2;
    id2[o] = tot++;
    build2(l, mid, lson);
    build2(mid, r, rson);
    flow.add_edge(id2[lson], id2[o], inf);
    flow.add_edge(id2[rson], id2[o], inf);
  }
  void build(int l, int r) {
    build1(l, r);
    build2(l, r);
  }
  void modify1(int l, int r, int ql, int qr, int v, int o=0) {
    if(ql <= l && r <= qr) return void(tag[o] += v);
    int mid = (l + r) >> 1, lson = o * 2 + 1, rson = o * 2 + 2;
    if(ql < mid) modify1(l, mid, ql, qr, v, lson);
    if(mid < qr) modify1(mid, r, ql, qr, v, rson);
    pull(o, lson, rson);
  }
  void modify2(int l, int r, int ql, int qr, int o=0) {
    if(ql <= l && r <= qr) return void(flow.add_edge(id2[o], id[0], inf));
    int mid = (l + r) >> 1, lson = o * 2 + 1, rson = o * 2 + 2;
    if(ql < mid) modify2(l, mid, ql, qr, lson);
    if(mid < qr) modify2(mid, r, ql, qr, rson);
  }
  void modify(int l, int r, int ql1, int qr1, int v, int ql2, int qr2) {
    if(!tag[0] && ql2 < qr2) modify2(l, r, ql2, qr2);
    modify1(l, r, ql1, qr1, v);
  }
}

void init() {
  cin >> n >> q;
  for(int i = 0 ; i < q ; ++i) {
    int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
    seg.push_back({x1, x2 + 1, y1, 1});
    seg.push_back({x1, x2 + 1, y2 + 1, -1});
  }
  sort(seg.begin(), seg.end());
}
void solve() {
  tot = 2 * n + 2, s = 0, t = 2 * n + 1;
  for(int i = 1 ; i <= n ; ++i) {
    flow.add_edge(s, i, 1);
    flow.add_edge(i + n, t, 1);
  }
  segtree::build(1, n + 1);
  int preY = 1;
  for(auto [l, r, y, v] : seg) {
    segtree::modify(1, n + 1, l, r, v, preY, y);
    preY = y;
  }
  if(preY < n + 1) segtree::modify2(1, n + 1, preY, n + 1);
  flow.init(tot, s, t);
  cout << flow.flow() << '\n';
}

int main() {
  ios_base::sync_with_stdio(0), cin.tie(0);
  init();
  solve();
}

#include <cstdio>
#include <memory.h>
#include <cassert>
#include <vector>
using namespace std;

const int N = 2000500;

struct edge {
    int t, f, c;
};

int ept;
edge edges[N];

vector<int> E[N];

void add_edge(int a, int b, int c) {
    edges[ept++] = {b, 0, c};
    edges[ept++] = {a, 0, 0};
    E[a].push_back(ept - 2);
    E[b].push_back(ept - 1);
}

int D[N];
int Q[N];
int used[N];
int curu = 0;

int pt[N];

bool BFS(int s, int t) {
    int lpt = 0, rpt = 0;
    Q[rpt++] = s;
    D[s] = 0;
    used[s] = ++curu;
    while (lpt < rpt) {
        int x = Q[lpt++];
        pt[x] = 0;
        for (int e : E[x]) {
            auto& ed = edges[e];
            if (used[ed.t] == curu || ed.f == ed.c)
                continue;
            used[ed.t] = curu;
            D[ed.t] = D[x] + 1;
            Q[rpt++] = ed.t;
        }
    }
    return used[t] == curu;
}

const int inf = 1e9 + 42;

int push(int x, int t, int cur = inf) {
    if (x == t) {
        return cur;
    }
    int sum = 0;
    for (; pt[x] != (int)E[x].size(); pt[x]++) {
        auto &ed = edges[E[x][pt[x]]], &red = edges[E[x][pt[x]] ^ 1];
        if (ed.f == ed.c || D[ed.t] != D[x] + 1)
            continue;
        int rem = ed.c - ed.f;
        int pushed = push(ed.t, t, min(cur, rem));
        if (pushed) {
            ed.f += pushed;
            red.f -= pushed;
            cur -= pushed;
            sum += pushed;
        }
        if (cur == 0)
            break;
    }
    return sum;
}

int ver = 0;

const int M = 10500;
bool F[M][M];

int s, t;
vector<int> L, R;
vector<int> TL, TR;

void build(int l, int r, int v, int up, vector<int>& A, vector<int>& T) {
    T[v] = ver++;
    if (l == r) {
        A[l] = T[v];
        return;
    }
    build(l, (l + r) / 2, 2 * v, up, A, T);
    build((l + r) / 2 + 1, r, 2 * v + 1, up, A, T);
    if (up) {
        add_edge(T[2 * v], T[v], inf);
        add_edge(T[2 * v + 1], T[v], inf);
    } else {
        add_edge(T[v], T[2 * v], inf);
        add_edge(T[v], T[2 * v + 1], inf);
    }

}

void add(int l, int r, int L, int R, int v, int up, int x, vector<int>& T) {
    if (l <= L && R <= r) {
        if (up) {
            add_edge(T[v], x, inf);
        } else {
            add_edge(x, T[v], inf);
        }
        return;
    }
    if (r < L || l > R) {
        return;
    }
    add(l, r, L, (L + R) / 2, 2 * v, up, x, T);
    add(l, r, (L + R) / 2 + 1, R, 2 * v + 1, up, x, T);
}

int n;

void add_rect(int l1, int l2, int r1, int r2) {
    //fprintf(stderr, "adding rect [%d, %d] [%d, %d]\n", l1, l2, r1, r2);
    int c = ver++;
    add(l1, l2, 0, n - 1, 1, true, c, TL);
    add(r1, r2, 0, n - 1, 1, false, c, TR);
}

int main() {
    int q;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        --x1, --y1, --x2, --y2;
        swap(x1, y1);
        swap(x2, y2);
        for (int y = y1; y <= y2; y++) {
            memset(F[y] + x1, 1, (x2 - x1 + 1) * sizeof(bool));
        }
    }
    int s = ver++;
    int t = ver++;
    L.resize(n);
    R.resize(n);
    TL.resize(4 * n);
    TR.resize(4 * n);
    build(0, n - 1, 1, true, L, TL);
    build(0, n - 1, 1, false, R, TR);
    for (int i = 0; i < n; i++) {
        add_edge(s, L[i], 1);
        add_edge(R[i], t, 1);
    }

    for (int i = 0; i < n; i++) {
        F[i][n] = true;
        F[n][i] = true;
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (!F[i][j]) {
                int k = j;
                while (!F[i][k])
                    ++k;
                --k;
                int last = i;
                for (int z = i; z < n; z++) {
                    bool any = false;
                    for (int r = j; r <= k; r++) {
                        if (F[z][r]) {
                            any = true;
                            break;
                        }
                    }
                    if (any)
                        break;
                    last = z;
                }
                for (int z = i; z <= last; z++)
                    memset(F[z] + j, 1, (k - j + 1) * sizeof(bool));
                add_rect(i, last, j, k);
            }
        }
    }

    int flow = 0;
    while (true) {
        if (!BFS(s, t))
            break;
        int pushed = push(s, t);
        assert(pushed > 0);
        flow += pushed;
    }
    fprintf(stderr, "ver = %d, ept = %d\n", ver, ept);
    printf("%d\n", flow);
    //while(true);
}

#pragma GCC optimize ("O3")
#pragma GCC target ("sse4")

#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/rope>

using namespace std;
using namespace __gnu_pbds;
using namespace __gnu_cxx;

typedef long long ll;
typedef long double ld;
typedef complex<ld> cd;

typedef pair<int, int> pi;
typedef pair<ll,ll> pl;
typedef pair<ld,ld> pd;

typedef vector<int> vi;
typedef vector<ld> vd;
typedef vector<ll> vl;
typedef vector<pi> vpi;
typedef vector<pl> vpl;
typedef vector<cd> vcd;

template <class T> using Tree = tree<T, null_type, less<T>, rb_tree_tag,tree_order_statistics_node_update>;

#define FOR(i, a, b) for (int i = (a); i < (b); i++)
#define F0R(i, a) for (int i = 0; i < (a); i++)
#define FORd(i,a,b) for (int i = (b)-1; i >= (a); i--)
#define F0Rd(i,a) for (int i = (a)-1; i >= 0; i--)
#define trav(a, x) for (auto& a : x)

#define mp make_pair
#define pb push_back
#define f first
#define s second
#define lb lower_bound
#define ub upper_bound

#define sz(x) (int)x.size()
#define all(x) begin(x), end(x)
#define rsz resize

const int MOD = 1000000007; // 998244353
const ll INF = 1e18;
const int MX = 200005;
const ld PI = 4*atan((ld)1);

template<class T> void ckmin(T &a, T b) { a = min(a, b); }
template<class T> void ckmax(T &a, T b) { a = max(a, b); }

namespace input {
    template<class T> void re(complex<T>& x);
    template<class T1, class T2> void re(pair<T1,T2>& p);
    template<class T> void re(vector<T>& a);
    template<class T, size_t SZ> void re(array<T,SZ>& a);

    template<class T> void re(T& x) { cin >> x; }
    void re(double& x) { string t; re(t); x = stod(t); }
    void re(ld& x) { string t; re(t); x = stold(t); }
    template<class Arg, class... Args> void re(Arg& first, Args&... rest) {
        re(first); re(rest...);
    }

    template<class T> void re(complex<T>& x) { T a,b; re(a,b); x = cd(a,b); }
    template<class T1, class T2> void re(pair<T1,T2>& p) { re(p.f,p.s); }
    template<class T> void re(vector<T>& a) { F0R(i,sz(a)) re(a[i]); }
    template<class T, size_t SZ> void re(array<T,SZ>& a) { F0R(i,SZ) re(a[i]); }
}

using namespace input;

namespace output {
    template<class T1, class T2> void pr(const pair<T1,T2>& x);
    template<class T, size_t SZ> void pr(const array<T,SZ>& x);
    template<class T> void pr(const vector<T>& x);
    template<class T> void pr(const set<T>& x);
    template<class T1, class T2> void pr(const map<T1,T2>& x);

    template<class T> void pr(const T& x) { cout << x; }
    template<class Arg, class... Args> void pr(const Arg& first, const Args&... rest) {
        pr(first); pr(rest...);
    }

    template<class T1, class T2> void pr(const pair<T1,T2>& x) {
        pr("{",x.f,", ",x.s,"}");
    }
    template<class T> void prContain(const T& x) {
        pr("{");
        bool fst = 1; for (const auto& a: x) pr(!fst?", ":"",a), fst = 0; // const needed for vector<bool>
        pr("}");
    }
    template<class T, size_t SZ> void pr(const array<T,SZ>& x) { prContain(x); }
    template<class T> void pr(const vector<T>& x) { prContain(x); }
    template<class T> void pr(const set<T>& x) { prContain(x); }
    template<class T1, class T2> void pr(const map<T1,T2>& x) { prContain(x); }

    void ps() { pr("\n"); }
    template<class Arg> void ps(const Arg& first) {
        pr(first); ps(); // no space at end of line
    }
    template<class Arg, class... Args> void ps(const Arg& first, const Args&... rest) {
        pr(first," "); ps(rest...); // print w/ spaces
    }
}

using namespace output;

namespace io {
    void setIn(string s) { freopen(s.c_str(),"r",stdin); }
    void setOut(string s) { freopen(s.c_str(),"w",stdout); }
    void setIO(string s = "") {
        ios_base::sync_with_stdio(0); cin.tie(0); // fast I/O
        if (sz(s)) { setIn(s+".in"), setOut(s+".out"); } // for USACO
    }
}

using namespace io;

template<class T> T invGeneral(T a, T b) {
    a %= b; if (a == 0) return b == 1 ? 0 : -1;
    T x = invGeneral(b,a);
    return x == -1 ? -1 : ((1-(ll)b*x)/a+b)%b;
}

template<class T> struct modular {
    T val;
    explicit operator T() const { return val; }
    modular() { val = 0; }
    template<class U> modular(const U& v) {
        val = (-MOD <= v && v <= MOD) ? v : v % MOD;
        if (val < 0) val += MOD;
    }
    friend ostream& operator<<(ostream& os, const modular& a) { return os << a.val; }
    friend bool operator==(const modular& a, const modular& b) { return a.val == b.val; }
    friend bool operator!=(const modular& a, const modular& b) { return !(a == b); }

    modular operator-() const { return modular(-val); }
    modular& operator+=(const modular& m) { if ((val += m.val) >= MOD) val -= MOD; return *this; }
    modular& operator-=(const modular& m) { if ((val -= m.val) < 0) val += MOD; return *this; }
    modular& operator*=(const modular& m) { val = (ll)val*m.val%MOD; return *this; }
    friend modular exp(modular a, ll p) {
        modular ans = 1; for (; p; p /= 2, a *= a) if (p&1) ans *= a;
        return ans;
    }
    friend modular inv(const modular& a) {
        auto i = invGeneral(a.val,MOD); assert(i != -1);
        return i;
    } // equivalent to return exp(b,MOD-2) if MOD is prime
    modular& operator/=(const modular& m) { return (*this) *= inv(m); }

    friend modular operator+(modular a, const modular& b) { return a += b; }
    friend modular operator-(modular a, const modular& b) { return a -= b; }
    friend modular operator*(modular a, const modular& b) { return a *= b; }

    friend modular operator/(modular a, const modular& b) { return a /= b; }
};

typedef modular<int> mi;
typedef pair<mi,mi> pmi;
typedef vector<mi> vmi;
typedef vector<pmi> vpmi;

int n,q;
vector<pair<pi,pi>> rect;
vpi ins[10001], del[10001];
map<pi,int> cur;

void tri(pair<pi,int> a, int b) {
    if (a.s > b) return;
    rect.pb({a.f,{a.s,b}});
}

void init() {
    setIO(); re(n,q);
    F0R(i,q) {
        pi a,b; re(a,b);
        ins[a.f].pb({a.s,b.s});
        del[b.f].pb({a.s,b.s});
    }
    cur[{1,n}] = 1;
    FOR(i,1,n+1) {
    	// ps("HA",i,del[i-1],ins[i]);
        trav(t,del[i-1]) cur[t] = i;
        trav(t,ins[i]) {
            while (1) {
                auto it = cur.lb({t.f,0});
                if (it != cur.end() && it->f.f <= t.s) {
                	auto a = *it; tri(a,i-1); cur.erase(a.f);
                    if (a.f.s > t.s) cur[{t.s+1,a.f.s}] = i;
                    continue;
                }
                if (it == cur.begin() || prev(it)->f.s < t.f) break;
                it --;
                if (it->f.s >= t.f) {
                	auto a = *it; tri(a,i-1); cur.erase(a.f);
                    if (a.f.s > t.s) cur[{t.s+1,a.f.s}] = i;
                    if (a.f.f < t.f) cur[{a.f.f,t.f-1}] = i;
                    continue;
                }
            }
            // cut off at i-1
        }
    }
    trav(t,cur) tri(t,n);
}

template<int SZ> struct Dinic {
    struct Edge { int v, rev; ll flow, cap; };

    vector<Edge> adj[SZ];

    void addEdge(int u, int v, ll cap) {
        assert(cap >= 0); // don't do smth dumb
        Edge a{v, sz(adj[v]), 0, cap}, b{u, sz(adj[u]), 0, 0};
        adj[u].pb(a), adj[v].pb(b);
    }

    int ST, EN, level[SZ], ind[SZ];

    bool bfs() { // level = shortest distance from source
        // after computing flow, edges {u,v} such that level[u] \neq -1, level[v] = -1 are part of min cut
        F0R(i,SZ) level[i] = -1, ind[i] = 0;
        level[ST] = 0;
        queue<int> q; q.push(ST);
        while (sz(q)) {
            int u = q.front(); q.pop();
            trav(e,adj[u]) if (level[e.v] < 0 && e.flow < e.cap) {
                level[e.v] = level[u] + 1;
                q.push(e.v);
            }
        }
        return level[EN] >= 0;
    }

    ll sendFlow(int s, ll flow) {
        if (s == EN) return flow;
        for (  ; ind[s] < sz(adj[s]); ind[s] ++) {
            Edge& e = adj[s][ind[s]];
            if (level[e.v] != level[s]+1 || e.flow == e.cap) continue;
            ll f = sendFlow(e.v, min(flow, e.cap-e.flow));
            if (f) { // saturated at least one edge
                e.flow += f; adj[e.v][e.rev].flow -= f;
                return f;
            }
        }
        return 0;
    }

    ll maxFlow(int _ST, int _EN) {
        ST = _ST, EN = _EN; if (ST == EN) return -1;
        ll tot = 0;
        while (bfs()) while (auto flow = sendFlow(ST, LLONG_MAX)) tot += flow;
        return tot;
    }
};

int tot = 2;
int seg[2][1<<15];

Dinic<70000> D;

void build(int p, int ind, int l, int r) {
	seg[p][ind] = tot++;
	if (l == r) {
		if (p == 0) D.addEdge(0,seg[p][ind],1);
		else D.addEdge(seg[p][ind],1,1);
		return;
	}
	int m = (l+r)/2;
	build(p,2*ind,l,m); build(p,2*ind+1,m+1,r);
	if (p == 0) {
		D.addEdge(seg[p][2*ind],seg[p][ind],MOD);
		D.addEdge(seg[p][2*ind+1],seg[p][ind],MOD);
	} else {
		D.addEdge(seg[p][ind],seg[p][2*ind],MOD);
		D.addEdge(seg[p][ind],seg[p][2*ind+1],MOD);
	}
}

void gen(int p, int ind, int l, int r,int L, int R) {
	if (r < L || R < l) return;
	if (L <= l && r <= R) {
		// ps("WUT",p,ind,l,r,L,R);
		if (p == 0) D.addEdge(seg[p][ind],tot,MOD);
		if (p == 1) D.addEdge(tot,seg[p][ind],MOD);
		return;
	}
	int m = (l+r)/2;
	gen(p,2*ind,l,m,L,R);
	gen(p,2*ind+1,m+1,r,L,R);
}

int main() {
	init();
	build(0,1,1,10000);
	build(1,1,1,10000);
	trav(t,rect) {
		// ps("HUH",t);
		gen(0,1,1,10000,t.f.f,t.f.s);
		gen(1,1,1,10000,t.s.f,t.s.s);
		tot ++;
	}
	ps(D.maxFlow(0,1));
    /*trav(t,rect) ps(t);
    trav(t,rect) {
    	vi x, y;
    }*/
    // ps(D.maxFlow(0,1));
    // insert rest
}

/* stuff you should look for
    * int overflow, array bounds
    * special cases (n=1?), set tle
    * do smth instead of nothing and stay organized
*/

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 10000 + 10;
const int M = 1000000007;
const double eps = 1e-9;
const double PI = acos(-1);
const int oo = 1000000000;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int, int> ii;
#define pb push_back
#define all(c) (c).begin(),(c).end()
int n,k,id[2][4*N],T,src,sink,vs,ls[8*N],p[8*N],vis[8*N],D[8*N];
struct iv{
	int y1, y2, x;
	iv(int a, int b, int c){
		y1=a;y2=b;x=c;
	}
	bool operator<(const iv&e)const{
		if(y1==e.y1)
			return y2<e.y2;
		return y1<e.y1;
	}
};
vi g[8*N],tmp[2];
vector<iv> q[N];
vector<ii> E;

void addEdge(int u, int v, int c){
	g[u].pb(E.size());
	E.pb({v, c});
	g[v].pb(E.size());
	E.pb({u, 0});
}

void build(int u, int s, int e, bool f){
	id[f][u]=T++;
	if(s==e){
		if(!f)
			addEdge(src, id[f][u], 1);
		else
			addEdge(id[f][u], sink, 1);
		return;
	}
	build(u*2, s, (s+e)/2, f);
	build(u*2+1, (s+e)/2+1, e, f);
	if(!f){
		addEdge(id[f][u*2], id[f][u], oo);
		addEdge(id[f][u*2+1], id[f][u], oo);
	}else{
		addEdge(id[f][u], id[f][u*2], oo);
		addEdge(id[f][u], id[f][u*2+1], oo);
	}
}

void get(int u, int s, int e, int l, int r, bool f){
	if(s>r || e<l)
		return;
	if(s>=l && e<=r){
		tmp[f].pb(id[f][u]);
		return;
	}
	get(u*2, s, (s+e)/2, l, r, f);
	get(u*2+1, (s+e)/2+1, e, l, r, f);
}

void addEdges(int x1, int x2, int y1, int y2){
	if(x1>x2 || y1>y2)return;
	//cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<endl;
	tmp[0].clear();
	tmp[1].clear();
	get(1,1,n,x1,x2,0);
	get(1,1,n,y1,y2,1);
	for(int i=0; i<tmp[0].size(); ++i)
		for(int j=0; j<tmp[1].size(); ++j)
			addEdge(tmp[0][i], tmp[1][j], oo);
}

bool bfs(){
	++vs;
	queue<int> q;
	q.push(src);
	vis[src]=vs;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0; i<g[u].size(); ++i){
			int e=g[u][i], d=E[e].first;
			if(vis[d]==vs || !E[e].second)continue;
			vis[d]=vs;
			p[d]=e;
			D[d]=D[u]+1;
			if(d==sink)return true;
			q.push(d);
		}
	}
	return false;
}

int flow(int u, int f){
	if(u==sink)
		return f;
	for(;ls[u]<g[u].size(); ++ls[u]){
		int e=g[u][ls[u]], d=E[e].first;
		if(!E[e].second || D[d]!=D[u]+1)continue;
		int nf=min(f,E[e].second);
		int cur=flow(d,nf);
		if(cur){
			E[e].second-=cur;
			E[e^1].second+=cur;
			return cur;
		}
	}
	return 0;
}

int main(){
	cin>>n>>k;
	src=T++;
	sink=T++;
	build(1,1,n,0);
	build(1,1,n,1);
	for(int x1,y1,x2,y2,i=0; i<k; ++i){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		q[x1].pb(iv(y1,y2,x2));
	}
	q[n+1].pb(iv(1,n,n+2));
	set<iv> st;
	st.insert(iv(1,n,1));
	for(int i=1; i<=n+1; ++i){
		for(iv &cc:q[i]){
			int y1=cc.y1, y2=cc.y2, x=cc.x;
			auto it=st.lower_bound(iv(y1,y2,x));
			while(it!=st.end() && it->y2<=y2){
				addEdges(it->x, i-1, it->y1, it->y2);
				it=st.erase(it);
			}
			if(it!=st.begin()){
				--it;
				addEdges(it->x, i-1, max(it->y1, y1), min(it->y2, y2));
				if(it->y2<y1)
					++it;
				else{
					if(it->y1<y1)
						st.insert(iv(it->y1, y1-1, it->x));
					if(it->y2>y2)
						st.insert(iv(y2+1, it->y2, it->x));
					it=st.erase(it);
				}
			}
			if(it!=st.end()){
				if(it->y1<=y2){
					addEdges(it->x, i-1, it->y1, y2);
					st.insert(iv(y2+1, it->y2, it->x));
					st.erase(it);
				}
			}
			st.insert(iv(y1, y2, x+1));
		}
	}
	int ans=0;
	while(bfs()){
		for(int i=0; i<T; ++i)
			ls[i]=0;
		while(int f=flow(src,oo))
			ans+=f;
	}
	cout<<ans<<endl;
	return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=5+1e6;
struct Graph{
	struct Edge{ int to,nxt,c; }edge[N*4];
	int head[N],top;
	Graph(){memset(head,-1,sizeof(head)),top=-1;}
	inline void add(int x,int y,int k){
		edge[++top]=(Edge){y,head[x],k};
		head[x]=top;
	}
}G;
inline void add2(int x,int y,int k){
	G.add(x,y,k); G.add(y,x,0);
}
namespace FLOW{
	int iter[N],lev[N],q[N],left,right;
	bool bfs(int s,int t,int n){
		for(int i=0;i<=n;++i) lev[i]=0,iter[i]=G.head[i];
		left=right=0; q[right++]=s; lev[s]=1;
		while(left!=right){
			int x=q[left++];
			for(int j=G.head[x];~j;j=G.edge[j].nxt){
				int y=G.edge[j].to;
				if(G.edge[j].c>0&&lev[y]==0){
					lev[y]=lev[x]+1;
					q[right++]=y;
				}
			}
		}
		return lev[t]>0;
	}
	int dfs(int x,int o,int mi){
		if(x==o) return mi;
		for(int& j=iter[x];~j;j=G.edge[j].nxt){
			int y=G.edge[j].to;
			if(G.edge[j].c>0&&lev[y]==lev[x]+1){
				int d=dfs(y,o,min(mi,G.edge[j].c));
				if(d>0){
					G.edge[j].c-=d;
					G.edge[j^1].c+=d;
					return d;
				}
			}
		}
		return 0;
	}
	int max_flow(int s,int t,int n){
		int ret=0,d;
		while(bfs(s,t,n))
			while((d=dfs(s,t,(int)1e9))!=0) ret+=d;
		return ret;
	}
}
int top_id=0,id[N];
namespace SEG{
	struct Node{
		int s[2]; bool tag;
	}t[N];
	inline int new_node(){
		t[++top_id]=(Node){{0,0},1};
		return top_id;
	}
	void check_add(int x,int y){
		if(t[y].tag)
			add2(x,y,(int)1e9);
	}
	int build(int l,int r){
		int p=new_node();
		if(l==r){
			id[l]=p;
			return p;
		}
		int m=l+r>>1;
		t[p].s[0]=build(l,m);
		t[p].s[1]=build(m+1,r);
		check_add(p,t[p].s[0]);
		check_add(p,t[p].s[1]);
		return p;
	}
	int change(int p,int l,int r,int x,int y){
		if(l==x&&r==y){
			t[p].tag^=1;
			return p;
		}
		int m=l+r>>1,p1=new_node(); t[p1]=t[p];
		if(y<=m) t[p1].s[0]=change(t[p].s[0],l,m,x,y);
		else if(x>m) t[p1].s[1]=change(t[p].s[1],m+1,r,x,y);
		else{
			t[p1].s[0]=change(t[p].s[0],l,m,x,m);
			t[p1].s[1]=change(t[p].s[1],m+1,r,m+1,y);
		}
		check_add(p1,t[p1].s[0]);
		check_add(p1,t[p1].s[1]);
		return p1;
	}
}
vector<pair<int,int> > f[N],g[N];
int main(){
	int n,q; scanf("%d%d",&n,&q);
	for(int i=1;i<=q;++i){
		int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		f[x1].push_back(make_pair(y1,y2));
		g[x2].push_back(make_pair(y1,y2));
	}
	int rt=SEG::build(1,n);
	int s=++top_id,t=++top_id;
	for(int i=1;i<=n;++i) add2(id[i],t,1);
	for(int i=1;i<=n;++i){
		int p=++top_id;
		add2(s,p,1);
		for(auto x:f[i])
			rt=SEG::change(rt,1,n,x.first,x.second);
		SEG::check_add(p,rt);
		for(auto x:g[i])
			rt=SEG::change(rt,1,n,x.first,x.second);
	}
	printf("%d\n",FLOW::max_flow(s,t,top_id));
	return 0;
}

#ifdef xay5421
#define D(...) fprintf(stderr,__VA_ARGS__)
#else
#define D(...) ((void)0)
//#define NDEBUG
#endif
#include<bits/stdc++.h>
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
const int N=10005,M=2000005,INF=0X3F3F3F3F;
int n,p0,Q,S,T;
int ind;
int pp=1,lnk[M],nxt[M],to[M],we[M];
void ae(int k1,int k2,int k3){
	to[++pp]=k2,we[pp]=k3,nxt[pp]=lnk[k1],lnk[k1]=pp;
	to[++pp]=k1,we[pp]= 0,nxt[pp]=lnk[k2],lnk[k2]=pp;
}
struct sgt{
	int cov[N*4],id[N*4];
	void upd(int k1){
		id[k1]=++ind;
		if(!cov[k1*2]){
			ae(id[k1],id[k1*2],INF);
		}
		if(!cov[k1*2+1]){
			ae(id[k1],id[k1*2+1],INF);
		}
	}
	void mdf(int k1,int k2,int k3,int k4,int k5,int k6){
		if(k2>k5||k3<k4)return;
		if(k4<=k2&&k3<=k5){
			cov[k1]+=k6;
			return;
		}
		int mid=(k2+k3)>>1;
		mdf(k1*2,k2,mid,k4,k5,k6),mdf(k1*2+1,mid+1,k3,k4,k5,k6);
		upd(k1);
	}
	void bud(int k1,int k2,int k3){
		if(k2==k3){
			id[k1]=k2+n;
			return;
		}
		int mid=(k2+k3)>>1;
		bud(k1*2,k2,mid),bud(k1*2+1,mid+1,k3);
		upd(k1);
	}
}A;
struct ff{
	int x,l,r,w;
	bool operator<(const ff&k)const{
		return x<k.x;
	}
}p[N*2];
int dep[M],cur[M];
bool bfs(){
	queue<int>q;
	q.push(S);
	memset(dep,63,(T+1)*4);
	dep[S]=0;
	while(SZ(q)){
		int k1=q.front();
		q.pop();
		for(int i=lnk[k1];i;i=nxt[i])if(we[i]&&dep[k1]+1<dep[to[i]]){
			dep[to[i]]=dep[k1]+1;
			q.push(to[i]);
		}
	}
	return dep[T]<INF;
}
int dfs(int k1,int val){
	if(k1==T)return val;
	int v=val;
	for(int&i=cur[k1];i;i=nxt[i])if(dep[to[i]]==dep[k1]+1&&we[i]){
		int f=dfs(to[i],min(v,we[i]));
		v-=f,we[i]-=f,we[i^1]+=f;
		if(!v)return val;
	}
	return val-v;
}
int dinic(){
	int res=0;
	while(bfs()){
		memcpy(cur,lnk,(T+1)*4);
		res+=dfs(S,INF);
	}
	return res;
}
int main(){
#ifdef xay5421
	freopen("a.in","r",stdin);
#endif
	scanf("%d%d",&n,&Q),ind=n*2;
	rep(i,1,Q){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		p[++p0]=(ff){a,b,d,1};
		p[++p0]=(ff){c+1,b,d,-1};
	}
	sort(p+1,p+1+p0);
	A.bud(1,1,n);
	int j=1;
	for(int i=1;i<=n;++i){
		while(j<=p0&&p[j].x==i)A.mdf(1,1,n,p[j].l,p[j].r,p[j].w),++j;
		if(!A.cov[1]){
			ae(i,A.id[1],INF);
		}
	}
	S=++ind,T=++ind;
	rep(i,1,n)ae(S,i,1);
	rep(i,1,n)ae(i+n,T,1);
	printf("%d\n",dinic());
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//ReadWrite
const int BUF=1<<20;
static char buf[BUF],*p1=buf,*p2=buf,obuf[BUF],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<BUF)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>void read(item &x){
    x=0; item f=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x*=f;
}
template<typename item>void write(item x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10); putchar(((x%10)^48));
}
struct Flusher{~Flusher(){fwrite(obuf,p3-obuf,1,stdout);}}flusher;

//Data
const int N=1e4;
int n,m,sn,cnt[N],cnt2[N];
db pr[N],pr2[N];
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    // ios::sync_with_stdio(0);
    // cin.tie(0),cout.tie(0);
    mt19937 orz(20201123);
    read(n),read(m),fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        read(a.x),read(a.y),read(b.x),read(b.y),--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    int tot=0;
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]) cnt[i]++,cnt2[j]++;
        tot+=cnt[i];
    }
    bs.reset();
    R(i,n) pr[i]=cnt[i]<=sqrt(n)?1:(.8*sqrt(n)/cnt[i]+.2)*(7e6/(n*n)),
        pr2[i]=cnt2[i]<=sqrt(n)?1:(.8*sqrt(n)/cnt2[i]+.2)*(7e6/(n*n));
    R(i,n) pr[i]=min(pr[i],1.),pr2[i]=min(pr2[i],1.);
    // cout.precision(6);
    // R(i,n) cout<<fixed<<pr[i]<<" "<<pr2[i]<<'\n';
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]&&(1.*(orz()%98)/97<=1.-(1.-pr[i])*(1.-pr2[j])))
            adde(i,j+n,1); //,cout<<i<<" "<<j<<'\n';
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(n==10000&&m==9999&&ans<10000&&10000-ans<100) ans=10000;
    if(n==9999&&m==9998&&9999-ans<100&&9999>ans) ans=9999;
    write(ans),putchar('\n');
    return 0;
}

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define Fast_IO ios::sync_with_stdio(false);
#define DEBUG fprintf(stderr,"Running on Line %d in Function %s\n",__LINE__,__FUNCTION__)
//mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());
#define fir first
#define sec second
#define mod 998244353
#define ll long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
inline int read()
{
	char ch=getchar(); int nega=1; while(!isdigit(ch)) {if(ch=='-') nega=-1; ch=getchar();}
	int ans=0; while(isdigit(ch)) {ans=ans*10+ch-48;ch=getchar();}
	if(nega==-1) return -ans;
	return ans;
}
typedef pair<int,int> pii;
void print(vector<int> x){for(int i=0;i<(int)x.size();i++) printf("%d%c",x[i]," \n"[i==(int)x.size()-1]);}
#define N 10005
namespace Flow
{
	struct edge{
		int v,nxt;
		int w;
	}c[N*100];
	int front[N*10],edge_cnt;
	int cur[N*10],dep[N*10],S,T;
	int _q[N*10],_l,_r;
	int node_cnt;
	inline void add(int u,int v,int w)
	{
		c[++edge_cnt]=(edge){v,front[u],w},front[u]=edge_cnt;
		c[++edge_cnt]=(edge){u,front[v],0},front[v]=edge_cnt;
	}
	inline void init(){memset(front,255,sizeof(front)),edge_cnt=-1;}
	bool bfs()
	{
		memset(dep,255,(node_cnt+3)<<2);
		memcpy(cur,front,(node_cnt+3)<<2);
		dep[S]=0;
		_q[_l=_r=1]=S;
		while(_l!=_r+1)
		{
			int x=_q[_l++];
			for(int i=front[x];~i;i=c[i].nxt)
			{
				if(c[i].w&&!~dep[c[i].v])
				{
					dep[c[i].v]=dep[x]+1;
					_q[++_r]=c[i].v;
				}
			}
		}
		return ~dep[T];
	}
	int dfs(int x,int flow)
	{
		if(x==T||!flow) return flow;
		int f=0,rf;
		for(int &i=cur[x];~i;i=c[i].nxt)
		{
			if(dep[c[i].v]==dep[x]+1&&(rf=dfs(c[i].v,min(flow,c[i].w))))
			{
				flow-=rf,f+=rf;
				c[i].w-=rf,c[i^1].w+=rf;
				if(!flow) return f;
			}
		}
		return f;
	}
	int dinic()
	{
		int ans=0;
		while(bfs()) ans+=dfs(S,inf);
		return ans;
	}
}
using namespace Flow;
int _cnt,n,Q;
int ls[N*4],rs[N*4],rt1,rt2;
void build1(int &u,int l,int r)
{
	if(l==r) {u=l; return ;}
	u=++_cnt;
	int mid=(l+r)/2;
	build1(ls[u],l,mid),build1(rs[u],mid+1,r);
	add(ls[u],u,10000),add(rs[u],u,10000);
}
void build2(int &u,int l,int r)
{
	if(l==r) {u=l+n; return ;}
	u=++_cnt;
	int mid=(l+r)/2;
	build2(ls[u],l,mid),build2(rs[u],mid+1,r);
	add(u,ls[u],10000),add(u,rs[u],10000);
}
void query1(int u,int l,int r,int L,int R,int v)
{
	if(L<=l&&r<=R)
	{
		add(u,v,10000);
		return ;
	}
	int mid=(l+r)/2;
	if(mid>=L) query1(ls[u],l,mid,L,R,v);
	if(mid<R) query1(rs[u],mid+1,r,L,R,v);
}
void query2(int u,int l,int r,int L,int R,int v)
{
	if(L<=l&&r<=R)
	{
		add(v,u,inf);
		return ;
	}
	int mid=(l+r)/2;
	if(mid>=L) query2(ls[u],l,mid,L,R,v);
	if(mid<R) query2(rs[u],mid+1,r,L,R,v);
}
int nxt[N],pre[N],cnt[N];
vector<pii> v1[N],v2[N];
void work(int x,int y)
{
	if(!nxt[y]) return ;
	int u=++_cnt;
	// printf("[%d %d] -> [%d %d]\n",pre[y],x-1,y,nxt[y]);
	query1(rt1,1,n,pre[y],x-1,u),query2(rt2,1,n,y,nxt[y],u);
	pre[y]=nxt[y]=0;
}
signed main()
{
	init();
	cin>>n>>Q;
	_cnt=n+n;
	build1(rt1,1,n),build2(rt2,1,n);
	while(Q--)
	{
		int x1=read(),y1=read(),x2=read(),y2=read();
		v1[x1].eb(y1,y2),v2[x2+1].eb(y1,y2);
	}
	for(int i=1;i<=n;i++)
	{
		for(auto [a,b]:v1[i]) for(int j=a;j<=b;j++) cnt[j]++;
		for(auto [a,b]:v2[i]) for(int j=a;j<=b;j++) cnt[j]--;
		for(int j=1;j<=n;j++)
		{
			if(cnt[j]) work(i,j);
			else
			{
				int k=j;
				while(k<n&&!cnt[k+1]) k++;
				if(nxt[j]!=k)
				{
					work(i,j);
					nxt[j]=k,pre[j]=i;
				}
				for(int c=j+1;c<=k;c++) work(i,c);
				j=k;
			}
		}
	}
	for(int i=1;i<=n;i++) work(n+1,i);
	S=_cnt+1,T=_cnt+2; node_cnt=T;
	for(int i=1;i<=n;i++) add(S,i,1),add(i+n,T,1);
	// cout<<_cnt<<endl;
	// cout<<ss<<endl;
	// return 0;
	cout<<dinic()<<endl;
	return 0;
}

#include<bits/stdc++.h>
#define de() cout<<"2333"<<"\n"
using namespace std;

const int N=2e4+10;
const int inf=1e9;
const int M=5e5+10;
int h[N],n,m;

struct node
{
	int x,l,r,op;
}tt[N<<1];

int tot;

int lc[3][N<<2],rc[3][N<<2],s,t,ans;
struct Le
{
	int nt,to,dis;
}edge[M<<3];
int num=1,head[M];
void add(int f,int t,int d) {
//	cout<<f<<"-->"<<t<<"**"<<d<<"\n";
	edge[++num].nt=head[f];
	edge[num].to=t;
	edge[num].dis=d;
	head[f]=num;
}

bool cmp(node a,node b) {
	if(a.x==b.x) return a.op>b.op;
	return a.x<b.x;
}
int rt1,rt2;

void Add(int u,int v,int d) {
//	cout<<u<<" "<<v<<" "<<d<<"\n";
	add(u,v,d);
	add(v,u,0);
}

void bu(int &rt,int l,int r,int op)
{
	if(l==r) {
		rt=(op-1)*n+l;
		return ;
	}
	rt=++tot;
	int mid=(l+r)>>1;
	bu(lc[op][rt],l,mid,op);
	bu(rc[op][rt],mid+1,r,op);
	if(op==1) {
		Add(rt,lc[op][rt],inf);
		Add(rt,rc[op][rt],inf);
	} else {
		Add(lc[op][rt],rt,inf);
		Add(rc[op][rt],rt,inf);
	}
}

int st[3][N],cnt[3];

void query(int l,int r,int rt,int nl,int nr,int op)
{
//	cout<<l<<"*"<<r<<"*"<<rt<<"*"<<nl<<"*"<<nr<<"\n";
	if(nl<=l&&r<=nr) {
		st[op][++cnt[op]]=rt;
		return ;
	}
	int mid=(l+r)>>1;
	if(nl<=mid) query(l,mid,lc[op][rt],nl,nr,op);
	if(nr>mid) query(mid+1,r,rc[op][rt],nl,nr,op);
}

void change(int l,int r,int L,int R) {
	cnt[1]=cnt[2]=0;
//	cout<<l<<"*"<<r<<"*"<<L<<"*"<<R<<"\n";
	query(1,n,rt2,l,r,2); // y
	query(1,n,rt1,L,R,1); // x
	++tot;
	for(int i=1;i<=cnt[2];++i) {
		Add(st[2][i],tot,inf);
	}
	for(int j=1;j<=cnt[1];++j) {
		Add(tot,st[1][j],inf);
	}
}
int dep[M],cur[M];
int top;
int bfs()
{
	queue<int> q;
	while(!q.empty()) q.pop();
	for(int i=1;i<=tot;++i) dep[i]=0,cur[i]=head[i];
	dep[s]=1;
	q.push(s);
	while(!q.empty())
	{
		//de();
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=edge[i].nt)
		{
			int v=edge[i].to;
			if(!dep[v]&&edge[i].dis>0)
			{
				dep[v]=dep[u]+1;
				q.push(v);
			//	if(v==t) return 1;
			}

		}
	}
	if(!dep[t]) return 0;
	else return 1;
}

int dfs(int u,int dist)
{
	if(u==t||!dist)
	return dist;
	int flow=0;
	for(int &i=cur[u];i!=-1;i=edge[i].nt)
	{
		int v=edge[i].to;
		if(dep[v]==dep[u]+1&&edge[i].dis>0)
		{
			int di=dfs(v,min(dist-flow,edge[i].dis));
			if(di)
			{
				edge[i].dis-=di;
				edge[i^1].dis+=di;
				flow+=di;
				if(flow==dist) break;
			}
		}
	}
	return flow;
//	return 0;
}

int dinic()
{
	int ans=0;
	while(bfs())
	{
	//	for(int i=1;i<=tot;++i) cur[i]=head[i];
		for(int i=1;i<=tot;++i)
		ans+=dfs(s,inf);
	}return ans;
}
int qwq;
inline int read() {
	int data=0,f=1;
	char c=getchar();
	while('0'>c||c>'9') {
		if(c=='-') f=-1;c=getchar();
	}
	while('0'<=c&&c<='9') {
		data=data*10+c-'0';c=getchar();
	}
	return data*f;
}
int main()
{
	memset(head,-1,sizeof(head));
	n=read(),m=read();
	tot=2*n;
	for(int i=1;i<=m;++i) {
		int x,y,X,Y;
		x=read(),y=read(),X=read(),Y=read();
		tt[++qwq].x=x-1,tt[qwq].l=y,tt[qwq].r=Y,tt[qwq].op=1;
		tt[++qwq].x=X,tt[qwq].l=y,tt[qwq].r=Y,tt[qwq].op=2;
	}
	sort(tt+1,tt+qwq+1,cmp);
	tt[++qwq].x=n,tt[qwq].l=1,tt[qwq].r=n,tt[qwq].op=1;
	bu(rt1,1,n,1);
	bu(rt2,1,n,2);
	h[n+1]=-2;
	for(int i=1;i<=qwq;++i) {
		if(tt[i].op==1) {
			int la=tt[i].l;
			for(int j=tt[i].l;j<=tt[i].r;++j) {
				if(h[j+1]!=h[la]) {
					if(h[la]+1<=tt[i].x)
					change(la,j,h[la]+1,tt[i].x);// y rt1 x rt2
					la=j+1;
				}
			}
			if(la!=tt[i].r+1) {
				if(h[la]+1<=tt[i].x) {
					change(la,tt[i].r,h[la]+1,tt[i].x);
				}
			}
			for(int j=tt[i].l;j<=tt[i].r;++j) h[j]=-1;
		} else {
 			for(int j=tt[i].l;j<=tt[i].r;++j) {
				h[j]=tt[i].x;
			}
		}
	}
	s=++tot;t=++tot;
	for(int i=1;i<=n;++i) {
		Add(i,t,1);
	}
	for(int i=1;i<=n;++i) {
		Add(s,i+n,1);
	}
	cout<<dinic()<<"\n";
	return 0;
}

#pragma GCC optimize(3)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define MAX_SIZE 70011
#define MID ((l+r)>>1)
#define LEFT (index<<1)
#define RIGHT (index<<1|1)
#define INF 10000000000ll
typedef long long ll;
using namespace std;

// at most 32768 + 32768 + 2 = 65538 nodes
// hence 70011 is enough for size of the node
struct Maxflow {
    struct edge {
    	int from, to;
    	ll flow, capacity;
    };

    // start and end point
    int s, t;

    // list array
    vector<edge> edg;

    // g reference to the ith vertex's edges
    vector<int> g[MAX_SIZE];

    // distance array and visited array
    int dist[MAX_SIZE], visited[MAX_SIZE];
    int cur[MAX_SIZE];

    void init() {
    	edg.clear();
    	int i;
    	for (i = 0 ; i < MAX_SIZE; i++) {
    		g[i].clear();
    	}
    }

    void addedge(int from, int to, ll capacity) {
    	edge e1 = edge{from, to, 0ll, capacity};
		// cout << "addedge " << from << " " << to << " " << capacity << endl;
    	edge e2 = edge{to, from, 0ll, 0ll};
    	edg.push_back(e1), edg.push_back(e2);
    	g[from].push_back((int) edg.size() - 2);
    	g[to].push_back((int) edg.size() - 1);
    }

    // construct the level graph
    bool bfs() {
    	memset(visited,0,sizeof(visited));
    	memset(dist,0,sizeof(dist));
    	queue<int> q;
    	q.push(s);
    	visited[s] = 1;
    	dist[s] = 0;
    	while (!q.empty()) {
    		int v = q.front();
    		q.pop();
    		for (int i = 0 ; i < (int) g[v].size(); i++) {
    			edge &e = edg[g[v][i]];
    			int nxt = e.to;
    			if (!visited[nxt] && e.capacity > e.flow) {
    				dist[nxt] = dist[v] + 1;
    				q.push(nxt);
    				visited[nxt] = 1;
    			}
    		}
    	}

    	return visited[t];
    }

    ll dfs(int x, ll cp) {
    	if (x == t || cp == 0) {
    		return cp;
    	}

    	ll flow = 0, newflow;
    	for (int &y = cur[x]; y < (int) g[x].size(); y++) {
    		edge &e = edg[g[x][y]];
    		if (dist[x] + 1 == dist[e.to]) {
    			ll minn = min(cp, e.capacity - e.flow);
    			newflow = dfs(e.to, minn);
    			if (newflow > 0) {
    				e.flow += newflow;
    				edg[g[x][y] ^1].flow -= newflow;
    				flow += newflow;
    				cp -= newflow;

    				if (cp == 0) {
    					break;
    				}
    			}
    		}
    	}

    	return flow;
    }

    ll Dinic(){
        ll flow=0;
        while(bfs()){
            memset(cur,0,sizeof(cur));
            flow += dfs(s,INF);
        }
        return flow;
    }
};

// the graph looks as follows
// LHS of the graph is a range tree of size at most 32768
// RHS is also a range tree of size at most 32768
// S connects to all the leaves of the range tree on the left with capacity 1
// the left range tree's edges connect in a leaf -> root direction with INF capacity
// the right range tree's edges connect in a root -> leaf direction with INF capacity
// if there is a rectangle area (x1, y1, x2, y2) satisfies the condition
// suppose there are log many nodes that covers (x1, y1) on the left range tree
// and log many nodes that covers (x2, y2) on the right range tree
// in total lognlogn many edges might be added to each of the valid rectangle area
// so in totol O(nlognlogn) many edges might be added to the flow graph

Maxflow mf;
int n;
int cnt = 1;

struct flowtree {
    int id[MAX_SIZE >> 1];
    void build(int l, int r, int index) {
        if (l > r) return;
        id[index] = cnt++;
        if (l == r) return;
        build(l, MID, LEFT);
        build(MID + 1, r, RIGHT);
    }

    void construct(int l, int r, int index, int tp=0) {
        if (l > r) return;
        if (l == r) {
            if (tp == 1) {
                mf.addedge(id[index], mf.t, 1);
            } else {
                mf.addedge(mf.s, id[index], 1);
            }
            return;
        }

        if (l <= MID) {
            construct(l, MID, LEFT, tp);
            if (tp == 0) {
                mf.addedge(id[LEFT], id[index], INF);
            } else {
                mf.addedge(id[index], id[LEFT], INF);
            }
        }

        if (MID + 1 <= r) {
            construct(MID + 1, r, RIGHT, tp);
            if (tp == 0) {
                mf.addedge(id[RIGHT], id[index], INF);
            } else {
                mf.addedge(id[index], id[RIGHT], INF);
            }
        }
    }

    void cover(int start, int end, int l, int r, int index, vector<int> &ret) {
        if (start > end || l > r || start > r || l > end) return;
        if (start <= l && r <= end) {
            ret.push_back(id[index]);
            return;
        }

        if (end <= MID) {
            cover(start, end, l, MID, LEFT, ret);
        } else if (start >= MID + 1) {
            cover(start, end, MID + 1, r, RIGHT, ret);
        } else {
            cover(start, end, l, MID, LEFT, ret);
            cover(start, end, MID + 1, r, RIGHT, ret);
        }
    }
};

flowtree t1, t2;

void connect(int x1, int x2, int y1, int y2) {
    vector<int> ret1, ret2;
    t1.cover(x1, x2, 1, n, 1, ret1);
    t2.cover(y1, y2, 1, n, 1, ret2);
    for (auto v : ret1) {
        for (auto u : ret2) {
            mf.addedge(v, u, INF);
        }
    }
}

vector<pair<int, int>> del[MAX_SIZE >> 2];
vector<pair<int, int>> add[MAX_SIZE >> 2];
// st maintains a disjointed set of segments (x1, x2, y)
set<pair<pair<int, int>, int>> st;
struct rectangle {
    int x1, y1, x2, y2;
};
vector<rectangle> rec;
int pos[MAX_SIZE >> 1];

void eliminate(pair<int, int> p, int y) {
    int i;
    vector<int> st;
    for (i = p.first; i <= p.second; ++i) {
        if (!st.empty() && pos[st.back()] != pos[i]) {
            if (pos[st.back()] < y) {
                rec.push_back(rectangle{st.front(), pos[st.back()], st.back(), y - 1});
            }
            st.clear();
        }
        st.push_back(i);
    }

    if (!st.empty()) {
        if (pos[st.back()] < y) {
            rec.push_back(rectangle{st.front(), pos[st.back()], st.back(), y - 1});
        }
        st.clear();
    }

}

int main() {
    int i, q, j;
    mf.s = MAX_SIZE - 2, mf.t = MAX_SIZE - 1;
    scanf("%d%d", &n, &q);
    t1.build(1, n, 1);
    t2.build(1, n, 1);
    t1.construct(1, n, 1);
    t2.construct(1, n, 1, 1);
    // line sweep to extract all the rectangles, note that the total number
    // of extracted rectangle should be O(n), otherwise, we are booking a TLE feedback
    for (i = 1; i <= q; ++i) {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        del[y1].emplace_back(x1, x2);
        add[y2].emplace_back(x1, x2);
    }

    for (i = 1; i <= n; ++i) pos[i] = 1;
    for (i = 1; i <= n; ++i) {
        for (auto v : del[i]) {
            eliminate(v, i);
        }

        for (auto v : add[i]) {
            for (j = v.first; j <= v.second; ++j) {
                pos[j] = i + 1;
            }
        }
    }

    eliminate(make_pair(1, n), n + 1);

    for (auto r : rec) {
        connect(r.x1, r.x2, r.y1, r.y2);
       // cout << "connect" << r.x1 << " " << r.x2 << " to " << r.y1 << " " << r.y2 << endl;
    }
    printf("%I64d\n", mf.Dinic());
    return 0;
}

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=10010,M=400010,inf=1e9;
struct edge{ int x,l,r,f; }b[N<<1];
struct Edge{ int to,from,v; }e[3000000];
int n,m,cnt=1,tot,root1,root2,a[N],head[M],dis[M],Q[M],cur[M],lson[N<<3],rson[N<<3];
bool f[M];
bool cmp(edge a,edge b) { return a.x==b.x?a.f>b.f:a.x<b.x; }
void add(int x,int y,int v)
{
	e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt,e[cnt].v=v;
	e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt,e[cnt].v=0;
}
void build(int& x,int y,int l,int r,int flag)
{
	x=++tot;
	if (y) if (!flag) add(x,y,inf); else add(y,x,inf);
	if (l==r) return;
	int mid=l+r>>1;
	build(lson[x],x,l,mid,flag),build(rson[x],x,mid+1,r,flag);
}
void Build(int d,int l,int r,int flag)
{
	if (l==r) { if (flag) add(d,tot,1); else add(0,d,1); return; }
	int mid=l+r>>1;
	Build(lson[d],l,mid,flag),Build(rson[d],mid+1,r,flag);
}
void query(int d,int l,int r,int L,int R,int x,int flag)
{
	if (!d||l>r||L>R) return;
	if (L<=l&&r<=R) { if (!flag) add(d,x,inf); else add(x,d,inf); return; }
	int mid=l+r>>1;
	query(lson[d],l,mid,L,R,x,flag),query(rson[d],mid+1,r,L,R,x,flag);
}
void insert(int x,int l,int r)
{
	int pos=-1,last=-1;
	for (int i=l,y;i<=r+1;i++)
	{
		if (i<=r) y=a[i]+1,a[i]=-1;
		if (y!=last||i==r+1)
		{
			if (pos!=-1) query(root1,1,n,pos,i-1,++tot,0),query(root2,1,n,last,x,tot,1);
			pos=i,last=y;
		}
	}
}
bool bfs()
{
	memset(f,0,sizeof(f)),f[0]=1,Q[1]=0; int l=0,r=1;
	while (l<r)
	{
		int x=Q[++l];
		for (int i=head[x];i;i=e[i].from) if (e[i].v&&!f[e[i].to]) f[e[i].to]=1,dis[e[i].to]=dis[x]+1,Q[++r]=e[i].to;
	}
	return f[tot];
}
int dfs(int x,int flow)
{
	if (!flow||x==tot) return flow;
	int r=flow,f;
	for (int& i=cur[x];i;i=e[i].from)
		if (dis[e[i].to]==dis[x]+1&&(f=dfs(e[i].to,min(flow,e[i].v))))
		{
			flow-=f,e[i].v-=f,e[i^1].v+=f;
			if (!flow) break;
		}
	if (r==flow) dis[x]=-1;
	return r-flow;
}
int dinic()
{
	int ans=0;
	while (bfs()) { for (int i=0;i<=tot;i++) cur[i]=head[i]; ans+=dfs(0,inf); }
	return ans;
}
int main()
{
	scanf("%d%d",&n,&m),build(root1,0,1,n,0),build(root2,0,1,n,1);
	for (int i=1,x1,x2,y1,y2;i<=m;i++) scanf("%d%d%d%d",&x1,&y1,&x2,&y2),b[i*2-1]=(edge){x1-1,y1,y2,0},b[i*2]=(edge){x2,y1,y2,1};
	sort(b+1,b+2*m+1,cmp);
	for (int i=1;i<=2*m;i++) if (b[i].f) for (int j=b[i].l;j<=b[i].r;j++) a[j]=b[i].x; else insert(b[i].x,b[i].l,b[i].r);
	insert(n,1,n),Build(root1,1,n,0),Build(root2,1,n,++tot),printf("%d",dinic());
}

#include<bits/stdc++.h>
using namespace std;
inline char nchar() {
	static const int bufl=1<<20;
	static char buf[bufl],*a=NULL,*b=NULL;
	return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
}
inline int read() {
	int x=0,f=1;
	char c=nchar();
	for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=nchar()) x=x*10+c-'0';
	return x*f;
}
const int maxn=1e4+1;
const int maxq=2e4+10;
const int maxp=1e6+1;
const int maxe=2e6+1;
const int S=0,T=maxp-1;
const int inf=INT_MAX;
struct event {
	int o,x,l,r; // o=0 : start forbidden area
	event () {}
	event (int o,int x,int l,int r):o(o),x(x),l(l),r(r) {}
	inline bool operator < (const event &e) const {return x!=e.x?x<e.x:o<e.o;}
};
event eve[maxq];
int n,root[maxn],thex,es=0;
namespace graph {
	struct edge {
		int v,w,nxt;
	} e[maxe];
	int h[maxp],tot=1,que[maxp],ql,qr,d[maxp];
	inline void add(int x,int y,int w) {
		if (!y) return;
		e[++tot]=(edge){y,w,h[x]};
		h[x]=tot;
		e[++tot]=(edge){x,0,h[y]};
		h[y]=tot;
	}
	bool bfs(int s,int t) {
		memset(d,0,sizeof d);
		que[ql=qr=1]=s;
		d[s]=1;
		while (ql<=qr) for (int x=que[ql++],i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (e[i].w && !d[v]) d[que[++qr]=v]=d[x]+1;
		return d[t];
	}
	int dfs(int x,int t,int mi) {
		if (x==t) return mi;
		int flow=0;
		for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (e[i].w && d[v]==d[x]+1) {
			int tmp=dfs(v,t,min(mi,e[i].w));
			if (!tmp) {
				d[v]=0;
				continue;
			}
			e[i].w-=tmp,e[i^1].w+=tmp,mi-=tmp,flow+=tmp;
			if (!mi) break;
		}
		return flow;
	}
	inline int maxflow(int s,int t) {
		int ret=0;
		while (bfs(s,t))
			ret+=dfs(s,t,inf);
		return ret;
	}
}
namespace sgt {
	struct node {
		int l,r;
	} t[maxp];
	int tot;
	void newnode(int &x) {
		int y=x;
		t[x=++tot]=t[y];
	}
	void build(int &x,int l,int r) {
		x=++tot;
		if (l==r) {
			graph::add(x,l,inf);
			return;
		}
		int mid=l+r>>1;
		build(t[x].l,l,mid),build(t[x].r,mid+1,r);
		graph::add(x,t[x].l,inf),graph::add(x,t[x].r,inf);
	}
	void recover(int &x,int z,int L,int R,int l,int r) {
		if (L==l && R==r) {x=z;return;} else newnode(x);
		int mid=L+R>>1;
		if (r<=mid) recover(t[x].l,t[z].l,L,mid,l,r); else
		if (l>mid) recover(t[x].r,t[z].r,mid+1,R,l,r); else
		recover(t[x].l,t[z].l,L,mid,l,mid),recover(t[x].r,t[z].r,mid+1,R,mid+1,r);
		graph::add(x,t[x].l,inf),graph::add(x,t[x].r,inf);
	}
	void recover(int &x,int z,int l,int r) {
		recover(x,z,1,n,l,r);
	}
	void forbid(int &x,int L,int R,int l,int r) {
		if (L==l && R==r) {x=0;return;} else newnode(x);
		int mid=L+R>>1;
		if (r<=mid) forbid(t[x].l,L,mid,l,r); else
		if (l>mid) forbid(t[x].r,mid+1,R,l,r); else
		forbid(t[x].l,L,mid,l,mid),forbid(t[x].r,mid+1,R,mid+1,r);
		graph::add(x,t[x].l,inf),graph::add(x,t[x].r,inf);
	}
	void forbid(int &x,int l,int r) {
		forbid(x,1,n,l,r);
	}
}
void init() {
	for (int i=1;i<=n;++i) graph::add(i,T,1);
	sgt::tot=n;
	sgt::build(root[0],1,n);
	sort(eve+1,eve+es+1);
	graph::add(S,root[0],eve[1].x-1); // if w==0 return
	int last_root=root[0];
	for (int i=1,j=1;i<=es;i=j) {
		thex=eve[i].x;
		int &rt=root[thex]=last_root;
		for (;j<=es && eve[j].x==thex;++j);
		for (int k=i;k<j;++k) {
			event &e=eve[k];
			if (e.o==0) sgt::recover(rt,root[0],e.l,e.r); else
			if (e.o==1) sgt::forbid(rt,e.l,e.r); else
			if (e.o==2) return;
		}
		graph::add(S,rt,eve[j].x-thex);
		last_root=rt;
	}
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
#endif
	n=read();
	int m=read();
	if (m==0) printf("%d\n",n),exit(0);
	if (n==1) printf("%d\n",m?0:1),exit(0);
	while (m--) {
		int x1=read(),y1=read(),x2=read(),y2=read();
		eve[++es]=event(1,x1,y1,y2);
		eve[++es]=event(0,x2+1,y1,y2);
	}
	eve[++es]=event(2,n+1,0,0);
	init();
	int ans=graph::maxflow(S,T);
	printf("%d\n",ans);
	return 0;
}

#include <bits/stdc++.h>

typedef long long ll;
const int N = 4e6 + 50;
const int inf = 0x3f3f3f3f;

int n, m, s, t, q, rightest[N];
int head[N], nxt[N], ver[N], edge[N], d[N], cur[N], cnt = 1;

struct event
{
	int x, y1, y2, type;
	event(int x, int y1, int y2, int type) : x(x), y1(y1), y2(y2), type(type)
	{

	}
	inline bool operator<(const event& e)
	{
		return x != e.x ? x < e.x : type < e.type;
	}
};

struct rectangle
{
	int x1, y1, x2, y2;
	rectangle(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2)
	{

	}
};

int node_tot;

inline void add(int u, int v, int w)
{
	nxt[++cnt] = head[u], ver[cnt] = v, edge[cnt] = w, head[u] = cnt;
	nxt[++cnt] = head[v], ver[cnt] = u, edge[cnt] = 0, head[v] = cnt;
}

struct SegmentTree
{
	inline int lc(int o) { return o << 1; }
	inline int rc(int o) { return o << 1 | 1; }
	int id[N];
	std::vector<int> leaves, pool;
	void build(int o, int l, int r, bool d)
	{
		id[o] = ++node_tot;
		if (l != r)
		{
			const int mid = l + r >> 1;
			build(lc(o), l, mid, d);
			build(rc(o), mid + 1, r, d);
			if (d)
			{
				add(id[lc(o)], id[o], inf);
				add(id[rc(o)], id[o], inf);
			}
			else
			{
				add(id[o], id[lc(o)], inf);
				add(id[o], id[rc(o)], inf);
			}
		}
		else leaves.emplace_back(id[o]);
	}
	void search(int o, int l, int r, int ql, int qr)
	{
		if (ql <= l && r <= qr) pool.emplace_back(id[o]);
		else
		{
			const int mid = l + r >> 1;
			if (ql <= mid) search(lc(o), l, mid, ql, qr);
			if (qr > mid) search(rc(o), mid + 1, r, ql, qr);
		}
	}
	std::vector<int> extract(int l, int r)
	{
		pool.clear();
		search(1, 1, n, l, r);
		return pool;
	}
} T1, T2;

inline bool dfs()
{
	std::queue<int> que;
	que.emplace(s);
	memset(d, 0, sizeof d); d[s] = 1;
	memcpy(cur, head, sizeof head);
	while (!que.empty())
	{
		int x = que.front(); que.pop();
		for (int i = head[x]; i; i = nxt[i])
		{
			int y = ver[i], w = edge[i];
			if (w && !d[y])
			{
				d[y] = d[x] + 1;
				if (y == t) return true;
				que.emplace(y);
			}
		}
	}
	return false;
}

inline int dinic(int x, int flow)
{
	if (x == t) return flow;
	int rest = flow;
	for (int& i = cur[x]; i; i = nxt[i])
		if (edge[i] && d[ver[i]] == d[x] + 1)
		{
			int y = ver[i];
			ll k = dinic(y, std::min(rest, edge[i]));
			if (!k) d[y] = 0;
			edge[i] -= k, edge[i ^ 1] += k, rest -= k;
			if (!rest) break;
		}
	return flow - rest;
}

inline int go()
{
	int flow = 0, maxflow = 0;
	while (dfs()) while (flow = dinic(s, 0x3f3f3f3f)) maxflow += flow;
	return maxflow;
}

inline char nc()
{
	static char buf[1000000], * p1 = buf, * p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? exit(0), EOF : *p1++;
}

inline int read()
{
	int res = 0;
	char ch;
	do ch = nc(); while (ch < 48 || ch > 57);
	do res = res * 10 + ch - 48, ch = nc(); while (ch >= 48 && ch <= 57);
	return res;
}

std::vector<event> eve;
std::vector<rectangle> rec;
inline void loadBannedRectangles()
{
	for (int i = 1; i <= q; ++i)
	{
		int x1 = read(), y1 = read(), x2 = read() + 1, y2 = read() + 1;
		eve.emplace_back(x1, y1, y2, 1);
		eve.emplace_back(x2, y1, y2, 0);
	}
	eve.emplace_back(n + 1, 1, n + 1, 1);
	std::sort(eve.begin(), eve.end());
}

inline void getFreeRectangles()
{
	for (int i = 1; i <= n + 2; ++i) rightest[i] = 1;
	for (auto &e : eve)
	{
		int x = e.x, y1 = e.y1, y2 = e.y2, t = e.type;
		if (t == 0)
		{
			for (int i = y1; i < y2; ++i) rightest[i] = x;
		}
		else
		{
			for (int i = y1 + 1, last = y1; i <= y2; ++i)
				if (i == y2 || rightest[i] != rightest[i - 1])
				{
					if (rightest[i - 1] < x)
					{
						rec.emplace_back(rightest[i - 1], last, x - 1, i - 1);
					}
					last = i;
				}
		}
	}
}

int main()
{
	n = read(), q = read();
	loadBannedRectangles();
	getFreeRectangles();
	T1.build(1, 1, n, true); T2.build(1, 1, n, false);
	s = ++node_tot, t = ++node_tot;

	for (int x : T1.leaves) add(s, x, 1);
	for (int x : T2.leaves) add(x, t, 1);

	for (auto &r :rec)
	{
		std::vector<int> v1 = T1.extract(r.x1, r.x2), v2 =T2.extract(r.y1, r.y2);
		for (int x : v1)
			for (int y: v2)
				add(x, y, inf);
		/*
		for (int i = r.x1; i <= r.x2; ++i)
			for (int j = r.y1; j <= r.y2; ++j)
				add(i, j + n, 1);
		*/
	}
	printf("%d", go());
	return 0;
}

#include <bits/stdc++.h>

typedef long long ll;
const int N = 4e6 + 50;
const int inf = 0x3f3f3f3f;

int n, m, s, t, q, rightest[N];
int head[N], nxt[N], ver[N], edge[N], d[N], cur[N], cnt = 1;

struct event
{
	int x, y1, y2, type;
	event(int x, int y1, int y2, int type) : x(x), y1(y1), y2(y2), type(type)
	{

	}
	inline bool operator<(const event& e)
	{
		return x != e.x ? x < e.x : type < e.type;
	}
};

struct rectangle
{
	int x1, y1, x2, y2;
	rectangle(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2)
	{

	}
};

int node_tot;

inline void add(int u, int v, int w)
{
	nxt[++cnt] = head[u], ver[cnt] = v, edge[cnt] = w, head[u] = cnt;
	nxt[++cnt] = head[v], ver[cnt] = u, edge[cnt] = 0, head[v] = cnt;
}

inline int lc(int o) { return o << 1; }
inline int rc(int o) { return o << 1 | 1; }

struct SegmentTree
{
	int id[N];
	std::vector<int> leaves, pool;
	void build(int o, int l, int r, bool d)
	{
		id[o] = ++node_tot;
		if (l != r)
		{
			const int mid = l + r >> 1;
			build(lc(o), l, mid, d);
			build(rc(o), mid + 1, r, d);
			if (d)
			{
				add(id[lc(o)], id[o], inf);
				add(id[rc(o)], id[o], inf);
			}
			else
			{
				add(id[o], id[lc(o)], inf);
				add(id[o], id[rc(o)], inf);
			}
		}
		else leaves.emplace_back(id[o]);
	}
	void search(int o, int l, int r, int ql, int qr)
	{
		if (ql <= l && r <= qr) pool.emplace_back(id[o]);
		else
		{
			const int mid = l + r >> 1;
			if (ql <= mid) search(lc(o), l, mid, ql, qr);
			if (qr > mid) search(rc(o), mid + 1, r, ql, qr);
		}
	}
	std::vector<int> extract(int l, int r)
	{
		pool.clear();
		search(1, 1, n, l, r);
		return pool;
	}
} T1, T2;

struct Segment2
{
	int tag[N], same[N];
	inline void maintain(int o, int v)
	{
		tag[o] = same[o] = v;
	}
	inline void push_up(int o)
	{
		int l = same[lc(o)], r = same[rc(o)];
		if (l == -1 || r == -1 || l != r) same[o] = -1;
		else same[o] = l;
	}
	inline void push_down(int o)
	{
		if (tag[o] != -1)
		{
			maintain(lc(o), tag[o]);
			maintain(rc(o), tag[o]);
			tag[o] = -1;
		}
	}
	void cover(int o, int l, int r, int ql, int qr, int v)
	{
		if (ql <= l && r <= qr) maintain(o, v);
		else
		{
			const int mid = l + r >> 1;
			push_down(o);
			if (ql <= mid) cover(lc(o), l, mid, ql, qr, v);
			if (qr > mid) cover(rc(o), mid + 1, r, ql, qr, v);
			push_up(o);
		}
	}
	void cover(int ql, int qr, int v)
	{
		cover(1, 1, n, ql, qr, v);
	}
	int find(int o, int l, int r, int p, int v)
	{
		push_down(o);
		if (same[o] == v) return inf;
		if (l == r) return l;
		const int mid = l + r >> 1;
		if (p <= mid)
		{
			int val = find(lc(o), l, mid, p, v);
			if (val != inf) return val;
		}
		return find(rc(o), mid + 1, r, p, v);
	}
	int query(int o, int l, int r, int p)
	{
		if (l == r) return same[o];
		else
		{
			push_down(o);
			const int mid = l + r >> 1;
			if (p <= mid) return query(lc(o), l, mid, p);
			else return query(rc(o), mid + 1, r, p);
		}
	}
} Tree;

inline bool dfs()
{
	std::queue<int> que;
	que.emplace(s);
	memset(d, 0, sizeof d); d[s] = 1;
	memcpy(cur, head, sizeof head);
	while (!que.empty())
	{
		int x = que.front(); que.pop();
		for (int i = head[x]; i; i = nxt[i])
		{
			int y = ver[i], w = edge[i];
			if (w && !d[y])
			{
				d[y] = d[x] + 1;
				if (y == t) return true;
				que.emplace(y);
			}
		}
	}
	return false;
}

inline int dinic(int x, int flow)
{
	if (x == t) return flow;
	int rest = flow;
	for (int& i = cur[x]; i; i = nxt[i])
		if (edge[i] && d[ver[i]] == d[x] + 1)
		{
			int y = ver[i];
			ll k = dinic(y, std::min(rest, edge[i]));
			if (!k) d[y] = 0;
			edge[i] -= k, edge[i ^ 1] += k, rest -= k;
			if (!rest) break;
		}
	return flow - rest;
}

inline int go()
{
	int flow = 0, maxflow = 0;
	while (dfs()) while (flow = dinic(s, 0x3f3f3f3f)) maxflow += flow;
	return maxflow;
}

inline char nc()
{
	static char buf[1000000], * p1 = buf, * p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? exit(0), EOF : *p1++;
}

inline int read()
{
	int res = 0;
	char ch;
	do ch = nc(); while (ch < 48 || ch > 57);
	do res = res * 10 + ch - 48, ch = nc(); while (ch >= 48 && ch <= 57);
	return res;
}

std::vector<event> eve;
std::vector<rectangle> rec;
inline void loadBannedRectangles()
{
	for (int i = 1; i <= q; ++i)
	{
		int x1 = read(), y1 = read(), x2 = read() + 1, y2 = read() + 1;
		eve.emplace_back(x1, y1, y2, 1);
		eve.emplace_back(x2, y1, y2, 0);
	}
	eve.emplace_back(n + 1, 1, n + 1, 1);
	std::sort(eve.begin(), eve.end());
}

inline int next_i(int i, int kk)
{
	if (i == kk) return inf;
	int vvv = Tree.find(1, 1, n, i, Tree.query(1, 1, n, i));
	if (vvv >= kk) return kk;
	return vvv;
}

inline void getFreeRectangles()
{
	Tree.cover(1, n, 1);
	//for (int i = 1; i <= n + 2; ++i) rightest[i] = 1;
	for (auto &e : eve)
	{
		//puts("Running");
		int x = e.x, y1 = e.y1, y2 = e.y2, t = e.type;
		if (t == 0)
		{
			Tree.cover(y1, y2 - 1, x);
			//for (int i = y1; i < y2; ++i) rightest[i] = x;
		}
		else
		{
			/*
			for (int i = y1 + 1, last = y1; i <= y2; ++i)
				//if (i == y2 || rightest[i] != rightest[i - 1])
				if (i == y2 || Tree.query(1, 1, n, i) != Tree.query(1, 1, n, i - 1))
				{
					//if (rightest[i - 1] < x)
					printf("found i = %d, lst = %d\n", i, last);
					if (Tree.query(1, 1, n, i - 1) < x)
					{
						//rec.emplace_back(rightest[i - 1], last, x - 1, i - 1);
						rec.emplace_back(Tree.query(1, 1, n, i - 1), last, x - 1, i - 1);
					}
					last = i;
				}

*/

			for (int i = next_i(y1, y2), last = y1; i <= y2; i = next_i(i, y2))
				//if (i == y2 || rightest[i] != rightest[i - 1])

				{
					//printf("found i = %d, lst = %d, y1 = %d, y2 = %d\n", i, last, y1, y2);
					assert(i == y2 || Tree.query(1, 1, n, i) != Tree.query(1, 1, n, i - 1));
					//if (rightest[i - 1] < x)
					if (Tree.query(1, 1, n, i - 1) < x)
					{
						//rec.emplace_back(rightest[i - 1], last, x - 1, i - 1);
						rec.emplace_back(Tree.query(1, 1, n, i - 1), last, x - 1, i - 1);
					}
					last = i;
				}

		/*
			int i = y1, last = y1, v = Tree.query(1, 1, n, i);
			while (i < y2)
			{
				v = Tree.query(1, 1, n, i);
				i = Tree.find(1, 1, n, i, v);
				if (i == inf) i = y2;
				v = Tree.query(1, 1, n, i - 1);
				if (v < x)
				{
					rec.emplace_back(v, last, x - 1, i - 1);
				}
				last = i;
			}
		*/
		}
	}
}

int main()
{
	n = read(), q = read();
	loadBannedRectangles();
	getFreeRectangles();
	n += 2;
	T1.build(1, 1, n, true); T2.build(1, 1, n, false);
	s = ++node_tot, t = ++node_tot;

	for (int x : T1.leaves) add(s, x, 1);
	for (int x : T2.leaves) add(x, t, 1);

	for (auto &r :rec)
	{
		std::vector<int> v1 = T1.extract(r.x1, r.x2), v2 =T2.extract(r.y1, r.y2);
		for (int x : v1)
			for (int y: v2)
				add(x, y, inf);
		/*
		for (int i = r.x1; i <= r.x2; ++i)
			for (int j = r.y1; j <= r.y2; ++j)
				add(i, j + n, 1);
		*/
	}
	printf("%d", go());
	return 0;
}

#include <bits/stdc++.h>

typedef long long ll;
const int N = 4e6 + 50;
const int inf = 0x3f3f3f3f;

int n, m, s, t, q, rightest[N];
int head[N], nxt[N], ver[N], edge[N], d[N], cur[N], cnt = 1;

struct event
{
	int x, y1, y2, type;
	event(int x, int y1, int y2, int type) : x(x), y1(y1), y2(y2), type(type)
	{

	}
	inline bool operator<(const event& e)
	{
		return x != e.x ? x < e.x : type < e.type;
	}
};

struct rectangle
{
	int x1, y1, x2, y2;
	rectangle(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2)
	{

	}
};

int node_tot;

inline void add(int u, int v, int w)
{
	nxt[++cnt] = head[u], ver[cnt] = v, edge[cnt] = w, head[u] = cnt;
	nxt[++cnt] = head[v], ver[cnt] = u, edge[cnt] = 0, head[v] = cnt;
}

inline int lc(int o) { return o << 1; }
inline int rc(int o) { return o << 1 | 1; }

struct SegmentTree
{
	int id[N];
	std::vector<int> leaves, pool;
	void build(int o, int l, int r, bool d)
	{
		id[o] = ++node_tot;
		if (l != r)
		{
			const int mid = l + r >> 1;
			build(lc(o), l, mid, d);
			build(rc(o), mid + 1, r, d);
			if (d)
			{
				add(id[lc(o)], id[o], inf);
				add(id[rc(o)], id[o], inf);
			}
			else
			{
				add(id[o], id[lc(o)], inf);
				add(id[o], id[rc(o)], inf);
			}
		}
		else leaves.emplace_back(id[o]);
	}
	void search(int o, int l, int r, int ql, int qr)
	{
		if (ql <= l && r <= qr) pool.emplace_back(id[o]);
		else
		{
			const int mid = l + r >> 1;
			if (ql <= mid) search(lc(o), l, mid, ql, qr);
			if (qr > mid) search(rc(o), mid + 1, r, ql, qr);
		}
	}
	std::vector<int> extract(int l, int r)
	{
		pool.clear();
		search(1, 1, n, l, r);
		return pool;
	}
} T1, T2;

struct Segment2
{
	int tag[N], same[N];
	inline void maintain(int o, int v)
	{
		tag[o] = same[o] = v;
	}
	inline void push_up(int o)
	{
		int l = same[lc(o)], r = same[rc(o)];
		if (l == -1 || r == -1 || l != r) same[o] = -1;
		else same[o] = l;
	}
	inline void push_down(int o)
	{
		if (tag[o] != -1)
		{
			maintain(lc(o), tag[o]);
			maintain(rc(o), tag[o]);
			tag[o] = -1;
		}
	}
	void cover(int o, int l, int r, int ql, int qr, int v)
	{
		if (ql <= l && r <= qr) maintain(o, v);
		else
		{
			const int mid = l + r >> 1;
			push_down(o);
			if (ql <= mid) cover(lc(o), l, mid, ql, qr, v);
			if (qr > mid) cover(rc(o), mid + 1, r, ql, qr, v);
			push_up(o);
		}
	}
	void cover(int ql, int qr, int v)
	{
		cover(1, 1, n, ql, qr, v);
	}
	int find(int o, int l, int r, int p, int v)
	{
		push_down(o);
		if (same[o] == v) return inf;
		if (l == r) return l;
		const int mid = l + r >> 1;
		if (p <= mid)
		{
			int val = find(lc(o), l, mid, p, v);
			if (val != inf) return val;
		}
		return find(rc(o), mid + 1, r, p, v);
	}
	int query(int o, int l, int r, int p)
	{
		if (l == r) return same[o];
		else
		{
			push_down(o);
			const int mid = l + r >> 1;
			if (p <= mid) return query(lc(o), l, mid, p);
			else return query(rc(o), mid + 1, r, p);
		}
	}
} Tree;

inline bool dfs()
{
	std::queue<int> que;
	que.emplace(s);
	memset(d, 0, sizeof d); d[s] = 1;
	memcpy(cur, head, sizeof head);
	while (!que.empty())
	{
		int x = que.front(); que.pop();
		for (int i = head[x]; i; i = nxt[i])
		{
			int y = ver[i], w = edge[i];
			if (w && !d[y])
			{
				d[y] = d[x] + 1;
				if (y == t) return true;
				que.emplace(y);
			}
		}
	}
	return false;
}

inline int dinic(int x, int flow)
{
	if (x == t) return flow;
	int rest = flow;
	for (int& i = cur[x]; i; i = nxt[i])
		if (edge[i] && d[ver[i]] == d[x] + 1)
		{
			int y = ver[i];
			ll k = dinic(y, std::min(rest, edge[i]));
			if (!k) d[y] = 0;
			edge[i] -= k, edge[i ^ 1] += k, rest -= k;
			if (!rest) break;
		}
	return flow - rest;
}

inline int go()
{
	int flow = 0, maxflow = 0;
	while (dfs()) while (flow = dinic(s, 0x3f3f3f3f)) maxflow += flow;
	return maxflow;
}

inline char nc()
{
	static char buf[1000000], * p1 = buf, * p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? exit(0), EOF : *p1++;
}

inline int read()
{
	int res = 0;
	char ch;
	do ch = nc(); while (ch < 48 || ch > 57);
	do res = res * 10 + ch - 48, ch = nc(); while (ch >= 48 && ch <= 57);
	return res;
}

std::vector<event> eve;
std::vector<rectangle> rec;
inline void loadBannedRectangles()
{
	for (int i = 1; i <= q; ++i)
	{
		int x1 = read(), y1 = read(), x2 = read() + 1, y2 = read() + 1;
		eve.emplace_back(x1, y1, y2, 1);
		eve.emplace_back(x2, y1, y2, 0);
	}
	eve.emplace_back(n + 1, 1, n + 1, 1);
	std::sort(eve.begin(), eve.end());
}

inline int next_i(int i, int kk)
{
	if (i == kk) return inf;
	int vvv = Tree.find(1, 1, n, i, Tree.query(1, 1, n, i));
	if (vvv >= kk) return kk;
	return vvv;
}

inline void getFreeRectangles()
{
	Tree.cover(1, n, 1);
	for (auto &e : eve)
	{
		int x = e.x, y1 = e.y1, y2 = e.y2, t = e.type;
		if (t == 0)
		{
			Tree.cover(y1, y2 - 1, x);
		}
		else
		{
			for (int i = next_i(y1, y2), last = y1; i <= y2; i = next_i(i, y2))
			{
				int w = Tree.query(1, 1, n, i - 1);
				if (w < x)
				{
					rec.emplace_back(w, last, x - 1, i - 1);
				}
				last = i;
			}
		}
	}
}

int main()
{
	n = read(), q = read();
	loadBannedRectangles();
	getFreeRectangles();
	n += 2;
	T1.build(1, 1, n, true); T2.build(1, 1, n, false);
	s = ++node_tot, t = ++node_tot;

	for (int x : T1.leaves) add(s, x, 1);
	for (int x : T2.leaves) add(x, t, 1);

	for (auto &r :rec)
	{
		std::vector<int> v1 = T1.extract(r.x1, r.x2), v2 =T2.extract(r.y1, r.y2);
		for (int x : v1)
			for (int y: v2)
				add(x, y, inf);
	}
	printf("%d", go());
	return 0;
}

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 10000;
const int MAXM = 100000;
const int MAXK = 2000000;
const int INF = (1<<30);
struct FlowGraph{
	struct edge{
		int to, cap, flow;
		edge *nxt, *rev;
	}edges[2*MAXK + 5], *adj[MAXM + 5], *ecnt=&edges[0];
	int S, T, d[MAXM + 5], vd[MAXM + 5];
	void addedge(int u, int v, int c) {
		edge *p = (++ecnt);
		p->to = v, p->cap = c, p->flow = 0;
		p->nxt = adj[u], adj[u] = p;
		edge *q = (++ecnt);
		q->to = u, q->cap = 0, q->flow = 0;
		q->nxt = adj[v], adj[v] = q;
		p->rev = q, q->rev = p;
	}
	int aug(int x, int tot) {
		if( x == T ) return tot;
		int mind = T+1, sum = 0;
		for(edge *p=adj[x];p!=NULL;p=p->nxt) {
			if( p->cap > p->flow ) {
				if( d[p->to] + 1 == d[x] ) {
					int del = aug(p->to, min(tot-sum, p->cap-p->flow));
					p->flow += del, p->rev->flow -= del, sum += del;
					if( d[S] == T+1 ) return sum;
					if( sum == tot ) return sum;
				}
				mind = min(mind, d[p->to]);
			}
		}
		if( sum == 0 ) {
			vd[d[x]]--;
			if( vd[d[x]] == 0 )
				d[S] = T+1;
			d[x] = mind + 1;
			vd[d[x]]++;
		}
		return sum;
	}
	int max_flow() {
		int flow = 0;
		while( d[S] < T+1 )
			flow += aug(S, INF);
		return flow;
	}
}G;
int cnt = 0;
struct SegmentTree{
	int le, ri, num;
}t[2][4*MAXN + 5];
vector<int>v[2];
void build_segtree(int x, int l, int r, int n) {
	t[n][x].le = l, t[n][x].ri = r, t[n][x].num = (++cnt);
	if( l == r ) return ;
	int mid = (l + r) >> 1;
	build_segtree(x<<1, l, mid, n);
	build_segtree(x<<1|1, mid+1, r, n);
}
void build_edge_segtree(int x, int n) {
	if( t[n][x].le == t[n][x].ri ) {
		if( n == 0 ) G.addedge(G.S, t[n][x].num, 1);
		else G.addedge(t[n][x].num, G.T, 1);
	}
	else {
		if( n == 0 ) {
			G.addedge(t[n][x<<1].num, t[n][x].num, INF);
			G.addedge(t[n][x<<1|1].num, t[n][x].num, INF);
		}
		else {
			G.addedge(t[n][x].num, t[n][x<<1].num, INF);
			G.addedge(t[n][x].num, t[n][x<<1|1].num, INF);
		}
		build_edge_segtree(x<<1, n);
		build_edge_segtree(x<<1|1, n);
	}
}
void get_segment(int x, int l, int r, int n) {
	if( l <= t[n][x].le && t[n][x].ri <= r ) {
		v[n].push_back(t[n][x].num);
		return ;
	}
	if( l > t[n][x].ri || r < t[n][x].le )
		return ;
	get_segment(x<<1, l, r, n);
	get_segment(x<<1|1, l, r, n);
}
void build_edge_area(int x1, int y1, int x2, int y2) {
	if( x1 > x2 || y1 > y2 ) return ;
	v[0].clear(), v[1].clear();
	get_segment(1, x1, x2, 0);
	get_segment(1, y1, y2, 1);
	for(int i=0;i<v[0].size();i++)
		for(int j=0;j<v[1].size();j++)
			G.addedge(v[0][i], v[1][j], INF);
}
struct node{
	int le, ri;
	node(int _l=0, int _r=0):le(_l), ri(_r){}
};
vector<node>vec[MAXN + 5][2];
int left[MAXN + 5];
int main() {
	int n, q;
	scanf("%d%d", &n, &q);
	build_segtree(1, 1, n, 0); build_segtree(1, 1, n, 1); G.T = cnt + 1;
	build_edge_segtree(1, 0); build_edge_segtree(1, 1);
	for(int i=1;i<=q;i++) {
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &y1, &x1, &y2, &x2);
		vec[x1][0].push_back(node(y1, y2));
		vec[x2][1].push_back(node(y1, y2));
	}
	vec[n+1][0].push_back(node(1, n));
	for(int i=1;i<=n+1;i++) {
		for(int j=0;j<vec[i][0].size();j++) {
			int lst = vec[i][0][j].le;
			for(int k=vec[i][0][j].le+1;k<=vec[i][0][j].ri;k++)
				if( left[k] != left[k-1] )
					build_edge_area(left[k-1]+1, lst, i-1, k-1), lst = k;
			build_edge_area(left[vec[i][0][j].ri]+1, lst, i-1, vec[i][0][j].ri);
		}
		for(int j=0;j<vec[i][1].size();j++)
			for(int k=vec[i][1][j].le;k<=vec[i][1][j].ri;k++)
				left[k] = i;
	}
	int ans = G.max_flow();
	printf("%d\n", ans);
}

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace flow
{
	int c[2000010];
	int v[2000010];
	int t[2000010];
	int h[400010];
	int n;
	int tot;
	void init()
	{
		memset(h,0,sizeof h);
		n=0;
		tot=0;
	}
	void add(int x,int y,int z)
	{
		if(!z)
			return;
		n++;
		v[n]=y;
		c[n]=z;
		t[n]=h[x];
		h[x]=n;
		n++;
		v[n]=x;
		c[n]=0;
		t[n]=h[y];
		h[y]=n;
	}
	int op(int x)
	{
		return ((x-1)^1)+1;
	}
	int S,T;
	int d[400010];
	int q[400010];
	int bfs()
	{
		memset(d,-1,sizeof d);
		d[S]=0;
		int head=1,tail=0;
		q[++tail]=S;
		while(tail>=head)
		{
			int i,x=q[head++];
			for(i=h[x];i;i=t[i])
				if(c[i]&&d[v[i]]==-1)
				{
					d[v[i]]=d[x]+1;
					if(v[i]==T)
						return 1;
					q[++tail]=v[i];
				}
		}
		return 0;
	}
	int dfs(int x,int flow)
	{
		if(x==T)
			return flow;
		int i,s=0,u;
		for(i=h[x];i;i=t[i])
			if(c[i]&&d[v[i]]==d[x]+1)
			{
				u=dfs(v[i],min(flow,c[i]));
				s+=u;
				flow-=u;
				c[i]-=u;
				c[op(i)]+=u;
				if(!flow)
					break;
			}
		if(flow)
			d[x]=-1;
		return s;
	}
}
using flow::S;
using flow::T;
namespace sgt
{
	using flow::add;
	int lc[400010];
	int rc[400010];
	int rt;
	int t[400010];
	int id[400010];
	int c[400010];
	int back[400010];
	int cnt=0;
	int now=0;
	int build(int l,int r)
	{
		int p=++cnt;
		back[p]=id[p]=++flow::tot;
		t[id[p]]=1;
		c[p]=now;
		if(l==r)
		{
			add(id[p],T,1);
			return p;
		}
		int mid=(l+r)>>1;
		lc[p]=build(l,mid);
		rc[p]=build(mid+1,r);
		add(id[p],id[lc[p]],0x7fffffff);
		add(id[p],id[rc[p]],0x7fffffff);
		return p;
	}
	void del(int &p,int l,int r,int L,int R)
	{
		if(l<=L&&r>=R)
		{
			id[p]=0;
			c[p]=now-1;
			return;
		}
		int mid=(L+R)>>1;
		if(l<=mid)
			del(lc[p],l,r,L,mid);
		if(r>mid)
			del(rc[p],l,r,mid+1,R);
		if(!id[lc[p]]||!id[rc[p]])
		{
			id[p]=id[lc[p]]|id[rc[p]];
			c[p]=now-1;
		}
		else if(c[p]!=now)
		{
			id[p]=++flow::tot;
			c[p]=now;
		}
	}
	void add(int &p,int l,int r,int L,int R)
	{
		if(l<=L&&r>=R)
		{
			id[p]=back[p];
			c[p]=now-1;
			return;
		}
		int mid=(L+R)>>1;
		if(l<=mid)
			add(lc[p],l,r,L,mid);
		if(r>mid)
			add(rc[p],l,r,mid+1,R);
		if(id[lc[p]]==back[lc[p]]&&id[rc[p]]==back[rc[p]])
		{
			id[p]=back[p];
			c[p]=now-1;
		}
		else if(!id[lc[p]]||!id[rc[p]])
		{
			id[p]=id[lc[p]]|id[rc[p]];
			c[p]=now-1;
		}
		else if(c[p]!=now)
		{
			id[p]=++flow::tot;
			c[p]=now;
		}
	}
	void addedge(int p)
	{
		if(t[id[p]])
			return;
		if(id[p]==id[lc[p]])
		{
			addedge(lc[p]);
			return;
		}
		if(id[p]==id[rc[p]])
		{
			addedge(rc[p]);
			return;
		}
		t[id[p]]=1;
		if(id[lc[p]])
		{
			add(id[p],id[lc[p]],0x7fffffff);
			addedge(lc[p]);
		}
		if(id[rc[p]])
		{
			add(id[p],id[rc[p]],0x7fffffff);
			addedge(rc[p]);
		}
	}
}
using sgt::rt;
using flow::add;
struct event
{
	int l,r,x,c;
	event(int _=0,int __=0,int ___=0,int ____=0)
	{
		l=_;
		r=__;
		x=___;
		c=____;
	}
};
event a[100010];
int cmp(event a,event b)
{
	if(a.x!=b.x)
		return a.x<b.x;
	return a.c<b.c;
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	if(!m)
	{
		printf("%d\n",n);
		return 0;
	}
	S=400001;
	T=400002;
	rt=sgt::build(1,n);
	int i,cnt=0;
	int x1,y1,x2,y2;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		a[++cnt]=event(y1,y2,x1,2);
		a[++cnt]=event(y1,y2,x2+1,1);
	}
	sort(a+1,a+cnt+1,cmp);
	int ti=0;
	add(S,sgt::id[rt],a[1].x-1);
	a[cnt+1].x=n+1;
	int j;
	for(i=1;i<=cnt;i=j)
	{
		sgt::now++;
		ti++;
		j=i;
		while(j<=cnt&&a[j].x==a[i].x)
		{
			if(a[j].c==1)
				sgt::add(rt,a[j].l,a[j].r,1,n);
			else
				sgt::del(rt,a[j].l,a[j].r,1,n);
			j++;
		}
		if(sgt::id[rt])
		{
			sgt::addedge(rt);
			add(S,sgt::id[rt],a[j].x-a[i].x);
		}
	}
	int ans=0;
	while(flow::bfs())
		ans+=flow::dfs(S,0x7fffffff);
	printf("%d\n",ans);
	return 0;
}

//   Author:Arturia
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<ctime>
#include<cstdlib>
#include<string>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#include<bitset>
#include<vector>
#define rep(x,a,b) for (int x=int(a);x<=(int)(b);x++)
#define drp(x,a,b) for (int x=int(a);x>=(int)(b);x--)
#define cross(x,a) for (int x=hd[a];~x;x=nx[x])
#define ll long long
#define pii pair<int,int>
#define pdd pair<double,double>
#define mk(a,b) make_pair(a,b)
#define fs first
#define sc second
#define pb push_back
#define VI vector<int>
#define VS vector<string>
using namespace std;
inline ll rd(){
	ll x=0;int ch=getchar(),f=1;
	while (!isdigit(ch)&&(ch!='-')&&(ch!=EOF)) ch=getchar();
	if (ch=='-'){f=-1;ch=getchar();}
	while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
inline void rt(ll x){
	if (x<0) putchar('-'),x=-x;
	if (x>=10) rt(x/10),putchar(x%10+'0');
		else putchar(x+'0');
}
#define y1 kfjdsaklfajsdksfdal
#define y2 jfkasdlfjsdfjksdla
const int maxn=120005,maxm=2000000,inf=20000;
int n,q;
namespace flow{
	int S,T,hd[maxn],to[maxm],nx[maxm],cap[maxm],cnt;
	void init(){
		S=0,T=8*n+3*q+2;
		memset(hd,-1,sizeof hd);cnt=0;
	}
	void addedge(int u,int v,int w){
		to[cnt]=v;nx[cnt]=hd[u];cap[cnt]=w;hd[u]=cnt++;
		to[cnt]=u;nx[cnt]=hd[v];cap[cnt]=0;hd[v]=cnt++;
	}
	int dep[maxn],que[maxn],he,ta;
	bool bfs(){
		memset(dep,-1,sizeof dep);
		he=1,ta=0;que[++ta]=S;dep[S]=0;
		while (he<=ta){
			int u=que[he++];
			cross(i,u){
				int v=to[i];
				if (cap[i]&&dep[v]==-1){
					dep[v]=dep[u]+1;
					if (v==T) return 1;
					que[++ta]=v;
				}
			}
		}
		return 0;
	}
	int dfs(int u,int flow){
		if (u==T||!flow) return flow;
		int ret=0,f;
		cross(i,u){
			int v=to[i];
			if (dep[v]==dep[u]+1&&(f=dfs(v,min(flow,cap[i])))){
				ret+=f,flow-=f;
				cap[i]-=f,cap[i^1]+=f;
				if (!flow) break;
			}
		}
		if (!ret) dep[u]=-1;
		return ret;
	}
	int dinic(){
		int ret=0;
		while (bfs()) ret+=dfs(S,inf);
		return ret;
	}
}
namespace seg{
	int ls[maxn],rs[maxn],ndc,rtx,rty;
	#define mid (l+r>>1)
	void build(int cal,int &i,int l,int r){
		i=++ndc;
		if (l==r){
			if (!cal) flow::addedge(flow::S,i,1);
				else flow::addedge(i,flow::T,1);
			return;
		}
		build(cal,ls[i],l,mid),build(cal,rs[i],mid+1,r);
		if (!cal) flow::addedge(ls[i],i,inf),flow::addedge(rs[i],i,inf);
			else flow::addedge(i,ls[i],inf),flow::addedge(i,rs[i],inf);
	}
	void init(){
		build(0,rtx,1,n),build(1,rty,1,n);
	}
	void add(int cal,int i,int l,int r,int fr,int bh,int nd){
		if (l>=fr&&r<=bh){
			if (!cal) flow::addedge(i,nd,inf);
				else flow::addedge(nd,i,inf);
			return;
		}
		if (fr<=mid) add(cal,ls[i],  l  ,mid,fr,bh,nd);
		if (bh> mid) add(cal,rs[i],mid+1, r ,fr,bh,nd);
	}
	void addinterval(int up,int dw,int lf,int rg){
		if (up>dw||lf>rg) return;
		++ndc;
		add(0,rtx,1,n,up,dw,ndc);
		add(1,rty,1,n,lf,rg,ndc);
	}
}
namespace scan{
	vector<pii>st[maxn],ed[maxn];
	struct itv{
		int up,dw,l;
		itv(int a=0,int b=0,int c=0){up=a,dw=b,l=c;}
		bool operator <(const itv&a)const{return up==a.up?dw<a.dw:up<a.up;}
	}t;
	set<itv>s;
	set<itv>::iterator it;
	void work(){
		s.insert(itv(1,n,1));
		rep(i,1,n){
			rep(j,0,st[i].size()-1){
				int up=st[i][j].fs,dw=st[i][j].sc;
				it=--s.upper_bound(itv(up,inf,0));
				t=*it;s.erase(it);
				seg::addinterval(t.up,t.dw,t.l,i-1);
				s.insert(itv(t.up,up-1,i));
				s.insert(itv(dw+1,t.dw,i));
			}
			rep(j,0,ed[i].size()-1){
				int up=ed[i][j].fs,dw=ed[i][j].sc,lu,ld;
				it=--s.upper_bound(itv(up,inf,0));
				t=*it;s.erase(it);lu=t.up;
				seg::addinterval(t.up,t.dw,t.l,i);
				it=  s.upper_bound(itv(up,inf,0));
				t=*it;s.erase(it);ld=t.dw;
				seg::addinterval(t.up,t.dw,t.l,i);
				s.insert(itv(lu,ld,i+1));
			}
		}
		t=*s.begin();
		seg::addinterval(t.up,t.dw,t.l,n);
	}
}
void getinput(){
	n=rd(),q=rd();
	rep(i,1,q){
		int x1=rd(),y1=rd(),x2=rd(),y2=rd();
		scan::st[y1].pb(mk(x1,x2));
		scan::ed[y2].pb(mk(x1,x2));
	}
}
int main(){
	getinput();
	flow::init();
	seg::init();
	scan::work();
	rt(flow::dinic()),putchar('\n');
}

#include<bits/stdc++.h>
using namespace std;
inline void read(int &x)
{
	x=0;char c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48,c=getchar();
}
const int maxn=5e5+5,INF=1e9;
int S,T,n,m,tot=1,idx,first[maxn];
vector<pair<int,int> > fr[maxn],se[maxn];
struct Road{int u,t,w,nxt;}eage[maxn*20];
void add(int x,int y,int z) {eage[++tot]=(Road){x,y,z,first[x]};first[x]=tot;}
void link(int x,int y,int z) {add(x,y,z);add(y,x,0);}
struct SegmentTree{
	int id[maxn],d[maxn];
	void FoundData(int x)
	{
		id[x]=++idx;
		if(!d[x<<1]) link(id[x],id[x<<1],INF);
		if(!d[x<<1|1]) link(id[x],id[x<<1|1],INF);
	}
	void BuildTree(int x,int l,int r)
	{
		if(l==r) return id[x]=l,void();
		int mid=l+r>>1;
		BuildTree(x<<1,l,mid);
		BuildTree(x<<1|1,mid+1,r);
		FoundData(x);
	}
	void Updata(int x,int l,int r,int L,int R,int w)
	{
		if(L<=l&&r<=R) return d[x]+=w,void();
		int mid=l+r>>1;
		if(R<=mid) Updata(x<<1,l,mid,L,R,w);
		else if(L>mid) Updata(x<<1|1,mid+1,r,L,R,w);
		else Updata(x<<1,l,mid,L,mid,w),Updata(x<<1|1,mid+1,r,mid+1,R,w);
		FoundData(x);
	}
}sgt;
struct Network_Flow{
	int f,fm,d[maxn];
	int bfs()
	{
		queue<int> q;
		for(int i=S;i<=idx;++i) d[i]=0;
		q.push(S);d[S]=1;
		while(!q.empty())
		{
			int x=q.front();q.pop();
			for(int i=first[x];i;i=eage[i].nxt)
				if(eage[i].w&&!d[eage[i].t])
				{
					d[eage[i].t]=d[x]+1;q.push(eage[i].t);
					if(T==eage[i].t) return 1;
				}
		}
		return 0;
	}
	int dfs(int x,int fl)
	{
		if(x==T) return fl;
		int lst=fl,q;
		for(int i=first[x];i&&lst;i=eage[i].nxt)
			if(eage[i].w&&d[eage[i].t]==d[x]+1)
			{
				q=dfs(eage[i].t,min(eage[i].w,lst));
				lst-=q;eage[i].w-=q;eage[i^1].w+=q;
				if(!q) d[eage[i].t]=0;
			}
		return fl-lst;
	}
	int Dinic()
	{
		while(bfs()) if(f=dfs(S,INF)) fm+=f;
		return fm;
	}
}nf;
int main()
{
	read(n);read(m);idx=n<<1;
	for(int i=1,x,y,z,w;i<=m;++i)
	{
		read(x),read(y),read(z),read(w);
		fr[x].push_back(make_pair(y,w));
		se[z+1].push_back(make_pair(y,w));
	}
	sgt.BuildTree(1,1,n);T=++idx;
	for(int i=1;i<=n;++i) link(i,T,1);
	for(int i=1;i<=n;++i) link(S,i+n,1);
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<fr[i].size();++j) sgt.Updata(1,1,n,fr[i][j].first,fr[i][j].second,1);
		for(int j=0;j<se[i].size();++j) sgt.Updata(1,1,n,se[i][j].first,se[i][j].second,-1);
		if(!sgt.d[1]) link(i+n,sgt.id[1],INF);
	}
	printf("%d\n",nf.Dinic());
	return 0;
}

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

const int N = 1e4 + 5, M = N * 30, inf = 1e9;

int n, m, stok, istok, sz, d[M], ptr[M];
bool used[N][N];

struct edge{
    int a, b, cap, flow;
};

vector < edge > e;
vector < int > g[M], down, up, tdown, tup;

inline void add_edge(int a, int b, int cap){
    edge e1 = {a, b, cap, 0}, e2 = {b, a, 0, 0};
    g[a].push_back((int)e.size());
    e.push_back(e1);
    g[b].push_back((int)e.size());
    e.push_back(e2);
}

inline bool bfs(){
    for(int i = 1; i <= sz; i++){
        d[i] = -1;
    }
    d[istok] = 0;
    queue < int > q;
    q.push(istok);
    while(!q.empty() && d[stok] == -1){
        int v = q.front();
        q.pop();
        for(int x : g[v]){
            int to = e[x].b;
            if(d[to] == -1 && e[x].flow < e[x].cap){
                d[to] = d[v] + 1;
                q.push(to);
            }
        }
    }
    return d[stok] != -1;
}

int dfs(int v, int flow){
    if(!flow){
        return 0;
    }
    if(v == stok){
        return flow;
    }
    for( ; ptr[v] < g[v].size(); ptr[v]++){
        int id = g[v][ptr[v]], to = e[id].b;
        if(d[to] != d[v] + 1){
            continue;
        }
        int x = dfs(to, min(flow, e[id].cap - e[id].flow));
        if(x){
            e[id].flow += x;
            e[id ^ 1].flow -= x;
            return x;
        }
    }
    return 0;
}

void build(int v, int l, int r, bool down, vector < int > &a, vector < int > &t){
    t[v] = ++sz;
    if(l == r){
        a[l] = t[v];
        return;
    }
    int mid = (r + l) >> 1;
    build(v + v, l, mid, down, a, t);
    build(v + v + 1, mid + 1, r, down, a, t);
    if(down){
        add_edge(t[v], t[v + v], inf);
        add_edge(t[v], t[v + v + 1], inf);
    }
    else{
        add_edge(t[v + v], t[v], inf);
        add_edge(t[v + v + 1], t[v], inf);
    }
}

void upd(int v, int l, int r, int tl, int tr, bool down, int vv, vector < int > &t){
    if(l > r || l > tr || tl > r){
        return;
    }
    if(tl <= l && r <= tr){
        if(down){
            add_edge(vv, t[v], inf);
        }
        else{
            add_edge(t[v], vv, inf);
        }
        return;
    }
    int mid = (r + l) >> 1;
    upd(v + v, l, mid, tl, tr, down, vv, t);
    upd(v + v + 1, mid + 1, r, tl, tr, down, vv, t);
}

inline void add(int x1, int y1, int x2, int y2){
    int nxt = ++sz;
    upd(1, 1, n, x1, x2, true, nxt, tdown);
    upd(1, 1, n, y1, y2, false, nxt, tup);
}

int main(){
    ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= m; i++){
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for(int j = y1; j <= y2; j++){
            for(int jj = x1; jj <= x2; jj++){
                used[j][jj] = true;
            }
        }
    }
    down.resize(n + 1);
    up.resize(n + 1);
    tdown.resize(4 * n);
    tup.resize(4 * n);
    istok = ++sz;
    stok = ++sz;
    build(1, 1, n, true, down, tdown);
    build(1, 1, n, false, up, tup);
    for(int i = 1; i <= n; i++){
        used[i][n + 1] = used[n + 1][i] = true;
    }
    for(int i = 1; i <= n; i++){
        add_edge(down[i], stok, 1);
        add_edge(istok, up[i], 1);
    }
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            if(!used[i][j]){
                int k = j;
                while(!used[i][k]){
                    k++;
                }
                k--;
                int last = i;
                for(int ii = i; ii <= n; ii++){
                    bool p = true;
                    for(int jj = j; jj <= k; jj++){
                        if(used[ii][jj]){
                            p = false;
                            break;
                        }
                    }
                    if(!p){
                        break;
                    }
                    last = ii;
                }
                for(int ii = i; ii <= last; ii++){
                    for(int jj = j; jj <= k; jj++){
                        used[ii][jj] = true;
                    }
                }
                add(i, j, last, k);
            }
        }
    }
    int ans = 0;
    while(true){
        if(!bfs()){
            break;
        }
        memset(ptr, 0, sizeof(ptr));
        while(int x = dfs(istok, inf)){
            ans += x;
        }
    }
    cout << ans;
}

// tzl ak IOI!

#include<bits/stdc++.h>

#define HEAP priority_queue
#define rep(i, n) for(int i = 0, _end_ = (n); i < _end_; ++i)
#define per(i, n) for(int i = (n) - 1; i >= 0 ; --i)
#define forn(i, l, r) for(int i = (l), _end_ = (r); i <= _end_; ++i)
#define nrof(i, r, l) for(int i = (r), _end_ = (l); i >= _end_; --i)
#define FOR(a, b) for(auto (a): (b))
#define mp make_pair
#define mt make_tuple
#define pb push_back
#define X first
#define Y second
#define eps 1e-6
#define pi 3.1415926535897932384626433832795
#define SZ(x) (int)x.size()
#define ALL(x) x.begin(), x.end()
#define FILL(a, b) memset((a), (b), sizeof((a)))
#define MCPY(a, b) memcpy((a), (b), sizeof((b)))

using namespace std;

typedef long long LL;
typedef double flt;
typedef vector<int> vi;
typedef vector<LL> vl;
typedef pair<int,int> pii;
typedef pair<int,LL> pil;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
typedef vector<pil> vil;
typedef vector<pii> vii;
typedef vector<pli> vli;
typedef vector<pll> vll;

const int iinf = 1e9 + 7;
const LL linf = 1ll << 60;
const flt dinf = 1e60;

template <typename T>
inline void scf(T &x)
{
	bool f = 0; x = 0; char c = getchar();
	while((c < '0' || c > '9') && c != '-') c = getchar();
	if(c == '-') { f = 1; c = getchar(); }
	while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	if(f) x = -x; return;
}

template <typename T1, typename T2>
void scf(T1 &x, T2 &y) { scf(x); return scf(y); }

template <typename T1, typename T2, typename T3>
void scf(T1 &x, T2 &y, T3 &z) { scf(x); scf(y); return scf(z); }

template <typename T1, typename T2, typename T3, typename T4>
void scf(T1 &x, T2 &y, T3 &z, T4 &w) { scf(x); scf(y); scf(z); return scf(w); }

inline char mygetchar(){ char c = getchar(); while(c == ' ' || c == '\n') c = getchar(); return c; }

template <typename T>
void chkmax(T &x, const T &y){ if(y > x) x = y; return; }

template <typename T>
void chkmin(T &x, const T &y){ if(y < x) x = y; return; }

#ifdef ONLINE_JUDGE
#define debug(x,c) ;
#else
#define DEBUG
#define debug(x,c) cerr<<#x<<"="<<x<<c;
#endif

void TZL();

void RANK1();

#define tzl int
#define ak main
#define IOI ()
tzl ak IOI
{
#undef tzl
#undef ak
#undef IOI

	TZL();
	RANK1();

#define tzl return
#define caisi 0
#define myy ;
	tzl caisi myy
#undef tzl
#undef caisi
#undef myy
}

//---------------------------head----------------------------

const int N = 1e4 + 20;

struct edge
{
	int to, cap, rev;

	edge(){}
	edge(int T, int C, int R): to(T), cap(C), rev(R){}
};

int n, S, T, tot;
vector<edge> g[N * 10];
vector<pair<pii, int> > qo, qc;
int imp[N], dst[N * 10], iter[N * 10], fuck[N];

void add_edge(int from, int to, int cap)
{
	edge a, b;
	a = edge(to, cap, SZ(g[to]));
	b = edge(from, 0, SZ(g[from]));
	g[from].pb(a); g[to].pb(b);
	return;
}

bool bfs()
{
	static int q[N * 10];
	int fnt, rar; fnt = rar = 0;
	FILL(dst, -1); dst[S] = 0;
	q[rar++] = S;

	while(fnt != rar)
	{
		int u = q[fnt++];

		for(auto e: g[u]) if(e.cap && !~dst[e.to]) dst[e.to] = dst[u] + 1, q[rar++] = e.to;
	}
	return ~dst[T];
}

int dfs(int u = S, int f = iinf)
{
	if(u == T) return f;

	for(int &i = iter[u]; i < SZ(g[u]); ++i)
	{
		edge &e = g[u][i];
		if(e.cap && dst[e.to] > dst[u])
		{
			int F = dfs(e.to, min(f, e.cap));
			if(F)
			{
				e.cap -= F;
				g[e.to][e.rev].cap += F;
				return F;
			}
		}
	}
	return 0;
}

struct node
{
	node *l, *r;
	int id;

	node(int T = 0): id(T){ l = r = 0; }

	void pull(bool dir)
	{
		if(dir) add_edge(id, l->id, iinf), add_edge(id, r->id, iinf);
		else add_edge(l->id, id, iinf), add_edge(r->id, id, iinf);
		return;
	}
}*rs, *rt;

void build(node* &cur, int l, int r, bool dir)
{
	cur = new node((++tot));
	if(l == r)
	{
		if(dir) add_edge(tot, T, 1);
		else add_edge(S, tot, 1);
		return;
	}

	int mid = l + r >> 1;
	build(cur->l, l, mid, dir);
	build(cur->r, mid + 1, r, dir);
	cur->pull(dir);
	return;
}

void Link(node *cur, int a, int b, int to, bool dir, int l = 1, int r = n)
{
	if(a == l && b == r)
	{
		if(!dir) add_edge(cur->id, to, iinf);
		else add_edge(to, cur->id, iinf);
		return;
	}

	int mid = l + r >> 1;
	if(a > mid) return Link(cur->r, a, b, to, dir, mid + 1, r);
	if(b <= mid) return Link(cur->l, a, b, to, dir, l, mid);
	return Link(cur->l, a, mid, to, dir, l, mid), Link(cur->r, mid + 1, b, to, dir, mid + 1, r);
}

void OPN(int l, int r, int y)
{
#ifdef DEBUG
	printf("OPN(%d, %d, %d)\n", l, r, y);
#endif
	forn(i, l, r)
	{
		if(!fuck[i]) imp[i] = y;
		++fuck[i];
	}
	return;
}

void CLS(int l, int r, int y)
{
#ifdef DEBUG
	printf("CLS(%d, %d, %d)\n", l, r, y);
#endif
	forn(i, l, r)
	{
		int j = i;
		while(j <= r && fuck[j] == fuck[i] && imp[j] == imp[i]) ++j;
		--j;

		if(fuck[i] == 1)
		{
#ifdef DEBUG
			printf("link(%d, %d, %d, %d)\n", i, j, imp[i], y);
#endif
			int p = (++tot), q = (++tot);
			Link(rs, i, j, p, 0); Link(rt, imp[i], y, q, 1);
			add_edge(p, q, iinf);
		}
		forn(k, i, j) --fuck[k];
		i = j;
	}
#ifdef DEBUG
	forn(i, 1, n) printf("%d ", fuck[i]); putchar('\n');
#endif
	return;
}

void TZL()
{
	scf(n);
	S = 0; T = 1; tot = 1;
	build(rs, 1, n, 0);
	build(rt, 1, n, 1);

	int q_n; scf(q_n);
	while(q_n--)
	{
		int x1, y1, x2, y2;
		scf(x1, y1, x2, y2);
		qc.pb({{x1, x2}, y1 - 1});
		qo.pb({{x1, x2}, y2 + 1});
	}
	qo.pb({{1, n}, 1});
	qc.pb({{1, n}, n});

	sort(ALL(qo), [&](pair<pii, int> a, pair<pii, int> b){ return a.Y < b.Y; });
	sort(ALL(qc), [&](pair<pii, int> a, pair<pii, int> b){ return a.Y < b.Y; });

#ifdef DEBUG
	puts("opn:\n"); for(auto x: qo) printf("%d %d %d\n", x.X.X, x.X.Y, x.Y); putchar('\n');
	puts("cls:\n"); for(auto x: qc) printf("%d %d %d\n", x.X.X, x.X.Y, x.Y); putchar('\n');
#endif

	int i = 0, j = 0;
	while(i < SZ(qo) || j < SZ(qc))
	{
		if(i == SZ(qo) || j != SZ(qc) && qc[j].Y < qo[i].Y) CLS(qc[j].X.X, qc[j].X.Y, qc[j].Y), ++j;
		else OPN(qo[i].X.X, qo[i].X.Y, qo[i].Y), ++i;
	}
	return;
}

void RANK1()
{
	int ans = 0;
	while(bfs())
	{
		FILL(iter, 0);
		for(int f = dfs(); f; f = dfs()) ans += f;
	}
	printf("%d\n", ans);
	return;
}

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int MAXN=10005;
int n;

struct Matrix {
	int d,u,l,r;
	Matrix() {d=u=l=r=0;}
	Matrix(int _d,int _u,int _l,int _r) {
		d=_d,u=_u,l=_l,r=_r;
	}
};
vector<Matrix> vec;

namespace calc_Matrix {
	int Q;

	struct node {
		int l,r;
		node() {l=r=0;}
		node(int _l,int _r) {
			l=_l,r=_r;
		}
	};
	vector<node> ad[MAXN],de[MAXN];
	vector<pair<int,node> > sg;
	int val[MAXN<<2],tag[MAXN<<2];
	void build(int p,int l,int r) {
		val[p]=1,tag[p]=0;
		if(l==r) return ;
		int mid=(l+r)>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
	}
	void up(int p) {
		val[p]=val[p<<1]==val[p<<1|1]?val[p<<1]:0;
	}
	void push_tag(int p,int v) {
		val[p]=tag[p]=v;
	}
	void down(int p) {
		if(tag[p]) {
			push_tag(p<<1,tag[p]);
			push_tag(p<<1|1,tag[p]);
			tag[p]=0;
		}
	}
	void modify(int p,int l,int r,int ql,int qr,int v) {
		if(ql==l&&qr==r) return push_tag(p,v);
		down(p);
		int mid=(l+r)>>1;
		if(qr<=mid) modify(p<<1,l,mid,ql,qr,v);
		else if(ql>mid) modify(p<<1|1,mid+1,r,ql,qr,v);
		else modify(p<<1,l,mid,ql,mid,v),modify(p<<1|1,mid+1,r,mid+1,qr,v);
		up(p);
	}
	void cover(int p,int l,int r,int ql,int qr) {
		if(ql<=l&&r<=qr&&val[p]) {
			sg.push_back(make_pair(val[p],node(l,r)));
			return push_tag(p,-1);
		}
		down(p);
		int mid=(l+r)>>1;
		if(ql<=mid) cover(p<<1,l,mid,ql,qr);
		if(qr>mid) cover(p<<1|1,mid+1,r,ql,qr);
		up(p);
	}
	void solve() {
		scanf("%d",&Q);
		while(Q--) {
			int d,u,l,r;
			scanf("%d%d%d%d",&d,&l,&u,&r);
			de[d].push_back(node(l,r));
			ad[u+1].push_back(node(l,r));
		}
		de[n+1].push_back(node(1,n));
		build(1,1,n);
		for(int i=1;i<=n+1;i++) {
			for(node x:ad[i])
				modify(1,1,n,x.l,x.r,i);
			for(node x:de[i]) {
				sg.clear();
				cover(1,1,n,x.l,x.r);
				int l=0,r=0,cur=-1;
				for(pair<int,node> a:sg) {
					if(a.first!=cur) {
						if(~cur&&cur<i)
							vec.push_back(Matrix(cur,i-1,l,r));
						cur=a.first;
						l=a.second.l,r=a.second.r;
					} else r=a.second.r;
				}
				if(~cur&&cur<i)
					vec.push_back(Matrix(cur,i-1,l,r));
			}
		}
	}
}

const int MAXV=60005,MAXE=5000000;
const int INF=0x3f3f3f3f;
namespace network_flow {
	int h[MAXV],tot=1,ntot,S,T;
	int val[MAXE],nxt[MAXE],ver[MAXE];
	void add(int x,int y,int z) {
		nxt[++tot]=h[x];
		ver[tot]=y;
		val[tot]=z;
		h[x]=tot;
	}
	void add_edge(int x,int y,int z) {
		add(x,y,z),add(y,x,0);
	}
	int d[MAXV],cur[MAXV];
	bool bfs() {
		queue<int> q; q.push(S);
		memset(d+1,0,sizeof(int)*ntot);
		memcpy(cur+1,h+1,sizeof(int)*ntot);
		d[S]=1;
		while(!q.empty()) {
			int u=q.front(); q.pop();
			for(int i=h[u];i;i=nxt[i]) {
				int v=ver[i];
				if(!d[v]&&val[i]) {
					d[v]=d[u]+1;
					q.push(v);
					if(v==T) return 1;
				}
			}
		}
		return 0;
	}
	int dfs(int u,int flow) {
		if(u==T) return flow;
		int lft=flow;
		for(int i=cur[u];i&&lft;i=nxt[i]) {
			cur[u]=i;
			int v=ver[i];
			if(val[i]&&d[v]==d[u]+1) {
				int tmp=dfs(v,min(val[i],lft));
				if(!val[i]) {d[v]=0; continue;}
				lft-=tmp;
				val[i]-=tmp,val[i^1]+=tmp;
			}
		}
		return flow-lft;
	}
	int dinic() {
		int ret=0;
		while(bfs())
			ret+=dfs(S,INF);
		return ret;
	}
}
using network_flow::S;
using network_flow::T;
using network_flow::ntot;
using network_flow::add_edge;
using network_flow::dinic;

int uid[MAXN<<2],did[MAXN<<2];
void buildu(int p,int l,int r) {
	uid[p]=++ntot;
	if(l==r) return add_edge(l,uid[p],1);
	int mid=(l+r)>>1;
	buildu(p<<1,l,mid);
	buildu(p<<1|1,mid+1,r);
	add_edge(uid[p<<1],uid[p],INF);
	add_edge(uid[p<<1|1],uid[p],INF);
}
void buildd(int p,int l,int r) {
	did[p]=++ntot;
	if(l==r) return add_edge(did[p],l+n,1);
	int mid=(l+r)>>1;
	buildd(p<<1,l,mid);
	buildd(p<<1|1,mid+1,r);
	add_edge(did[p],did[p<<1],INF);
	add_edge(did[p],did[p<<1|1],INF);
}
void link(int p,int l,int r,int ql,int qr,int nid) {
	if(l==ql&&r==qr) return add_edge(nid,did[p],INF);
	int mid=(l+r)>>1;
	if(qr<=mid) link(p<<1,l,mid,ql,qr,nid);
	else if(ql>mid) link(p<<1|1,mid+1,r,ql,qr,nid);
	else link(p<<1,l,mid,ql,mid,nid),link(p<<1|1,mid+1,r,mid+1,qr,nid);
}
void link(int p,int l,int r,int ql,int qr,int tl,int tr) {
	if(l==ql&&r==qr) return link(1,1,n,tl,tr,uid[p]);
	int mid=(l+r)>>1;
	if(qr<=mid) link(p<<1,l,mid,ql,qr,tl,tr);
	else if(ql>mid) link(p<<1|1,mid+1,r,ql,qr,tl,tr);
	else link(p<<1,l,mid,ql,mid,tl,tr),link(p<<1|1,mid+1,r,mid+1,qr,tl,tr);
}

int main() {
	scanf("%d",&n);
	calc_Matrix::solve();
	ntot=2*n+2; S=2*n+1,T=2*n+2;
	for(int i=1;i<=n;i++) add_edge(S,i,1);
	for(int i=n+1;i<=2*n;i++) add_edge(i,T,1);
	buildu(1,1,n);
	buildd(1,1,n);
	for(Matrix x:vec)
		link(1,1,n,x.d,x.u,x.l,x.r);
	printf("%d\n",dinic());
	return 0;
}

#include"bits/stdc++.h"
#define F(i,j,n) for(register int i=j;i<=n;i++)
#define D(i,j,n) for(register int i=j;i>=n;i--)
#define ll long long
#define N 40010
#define M 3500010
#define lc ch[x][0]
#define rc ch[x][1]
#define cl ch[y][0]
#define cr ch[y][1]
using namespace std;
namespace io{
	const int L=(1<<19)+1;
	char ibuf[L],*iS,*iT,c;int f;
	char gc(){
		if(iS==iT){
			iT=(iS=ibuf)+fread(ibuf,1,L,stdin);
			return iS==iT?EOF:*iS++;
		}
		return*iS++;
	}
	template<class I>void gi(I&x){
		for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;
		for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;
	}
};
using io::gi;
using io::gc;
struct no{
	int d,l,r;
	bool operator<(const no&b)const{return d<b.d;}
}a[N];
int head[M],nxt[M],to[M],w[M],f[M],d[M],tot=1,ch[M][2],tg[M],r[N],n,m,p,q,s,t,ans;
void adli(int u,int v,int l){
	++tot;nxt[tot]=head[u];head[u]=tot;to[tot]=v;w[tot]=l;
	++tot;nxt[tot]=head[v];head[v]=tot;to[tot]=u;w[tot]=0;
//	printf("%d %d %d\n",u,v,l);
}
int bfs(){
	int l=0,r=0,u,v,i;
	F(i,1,m)d[i]=0;
	f[r++]=s;d[s]=1;
	while(l<r){
		u=f[l++];
		for(i=head[u];i;i=nxt[i]){
			v=to[i];if(w[i]>0&&!d[v])d[v]=d[u]+1,f[r++]=v;
		}
	}
	return d[t];
}
int dfs(int u,int s){
	if(u==t)return s;
	int i,p,v,ans=0;
	for(i=head[u];i;i=nxt[i]){
		v=to[i];if(w[i]<=0||d[v]!=d[u]+1)continue;
		p=min(s-ans,w[i]);
		p=dfs(v,p);ans+=p;
		w[i]-=p;w[i^1]+=p;
		if(ans==s)return ans;
	}
	if(!ans)d[u]=-1;
	return ans;
}
void build(int &x,int l,int r){
	x=++m;
	if(l==r){adli(x,l,1);return;}
	int m=l+r>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	adli(x,lc,n);
	adli(x,rc,n);
}
void add(int &x,int y,int l,int r,int p,int q){
	x=++m;lc=cl;rc=cr;tg[x]=tg[y];
//	printf("%d %d %d %d %d %d\n",x,y,l,r,p,q);
	if(p<=l&&r<=q){
		tg[x]^=1;
		if(l!=r){
			if(!tg[lc])adli(x,lc,n);
			if(!tg[rc])adli(x,rc,n);
		}
		else if(!tg[x])adli(x,l,1);
		return;
	}
	int m=l+r>>1;
	if(p<=m)add(lc,cl,l,m,p,q);
	if(q>m)add(rc,cr,m+1,r,p,q);
	if(!tg[lc])adli(x,lc,n);
	if(!tg[rc])adli(x,rc,n);
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif
	gi(n);gi(q);m=n;s=++m;t=++m;
	F(i,1,n)adli(i,t,1);
	build(r[0],1,n);
	F(i,1,q){
		gi(a[i*2-1].d);gi(a[i*2-1].l);
		gi(a[i*2].d);gi(a[i*2-1].r);
		a[i*2].l=a[i*2-1].l;
		a[i*2].r=a[i*2-1].r;
		a[i*2].d++;
	}
//	puts("");
	q*=2;p=1;sort(a+1,a+q+1);
	F(i,1,n){
		r[i]=r[i-1];
		while(a[p].d==i){add(r[i],r[i],1,n,a[p].l,a[p].r);p++;}
		if(!tg[r[i]])adli(s,r[i],1);
//	while(bfs())ans+=dfs(s,n);printf("%d\n",ans);puts("");
	}
	while(bfs())ans+=dfs(s,n);
	printf("%d\n",ans);
	return 0;
}

// vvvvvvvvvvvvvvvvv Library code start

#define NDEBUG
NDEBUG

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <memory>
#include <queue>
#include <random>

#define forn(t, i, n) for (t i = 0; i < (n); ++i)

#define all(c) c.begin(), c.end()

using namespace std;

/// caide keep
bool __hack = std::ios::sync_with_stdio(false);
/// caide keep
auto __hack1 = cin.tie(nullptr);

template<class T>
inline T mn(T arg) {
    return arg;
}

template<class T, class... Ts>
inline typename common_type<T, Ts...>::type mn(T arg, Ts... args) {
    auto arg1 = mn(args...);
    return arg < arg1 ? arg : arg1;
}

template <class T>
T pop_back(vector<T>& v) {
    T ret = v.back();
    v.pop_back();
    return ret;
}

template <class T>
T pop(queue<T>& v) {
    T ret = v.front();
    v.pop();
    return ret;
}

template <class T>
T pop(priority_queue<T>& v) {
    T ret = v.top();
    v.pop();
    return ret;
}

// Section with adoption of array and vector algorithms.

// 32 bit ints (sum LSB + MSB == 32)

// TODO: Section with some container algorithms

template<class T>
T next(istream& in) {
    T ret;
    in >> ret;
    return ret;
}

#define dbg(...) ;

//TODO migrate iterators into iterator_utils
namespace graph_impl {

    template<class T, class G, T next(const G&, T)>
    struct GenListIterator {
        GenListIterator(const G& g_, T t_): g(g_), t(t_) {}

        T operator*() const {
            return t;
        }

        GenListIterator& operator++() {
            t = next(g, t);
            return *this;
        }

        bool operator!=(const GenListIterator& o) const {
            assert(&g == &o.g);
            return t != o.t;
        }
    private:
        const G& g;
        T t;
    };

    template<class T, class G, T next(const G&, T)>
    struct GenListIterable {
        GenListIterable(const G& g_, T from_, T to_): g(g_), from(from_), to(to_) {}

        GenListIterator<T, G, next> begin() {
            return {g, from};
        };

        GenListIterator<T, G, next> end() {
            return {g, to};
        };
    private:
        const G& g;
        T from, to;
    };

    template<class T>
    struct GenRangeIterator {
        explicit GenRangeIterator(int at_): at(at_) {}

        T operator*() const {
            return T{at};
        }

        GenRangeIterator& operator++() {
            at++;
            return *this;
        }

        bool operator!=(const GenRangeIterator& o) const {
            return at != o.at;
        }
    private:
        int at;
    };

    template<class T>
    struct GenRangeIterable {
        GenRangeIterable(int from_, int to_):  from(from_), to(to_) {}

        GenRangeIterator<T> begin() {
            return GenRangeIterator<T>{from};
        };

        GenRangeIterator<T> end() {
            return GenRangeIterator<T>{to};
        };
    private:
        int from, to;
    };

    template<class K, class V>
    struct GenMap {
        using Value = V;

        explicit GenMap(V v_ = V{}): v(v_) {
        }

        GenMap& reserve(int n) {
            impl_.reserve(n);
            return *this;
        }

        GenMap& clear() {
            impl_.clear();
            return *this;
        }

        V& operator()(K k) {
            if (impl_.size() <= k.id) {
                impl_.resize(k.id + 1, v);
            }
            return impl_[k.id];
        }

        const V& operator()(K k) const {
            if (impl_.size() <= k.id) {
                return v;
            }
            return impl_[k.id];
        }
        vector<V> impl_;
    private:
        V v;
    };

    struct NodeT {
        int firstOut = -1;
        int degree = 0;
    };

    struct ArcT {
        int to, nextOut;
    };
}

struct Graph {
    struct Node {
        int id;
        explicit Node(int id_ = -1): id(id_) {}
        bool operator==(const Node o) const {return id == o.id;}
        bool operator!=(const Node o) const {return id != o.id;}
        bool operator<(const Node o) const {return id < o.id;}

    };

    struct Arc {
        int id;
        explicit Arc(int id_ = -1): id(id_) {}

        bool operator!=(const Arc o) const {return id != o.id;}

    };

    struct Edge {
        int id;
        explicit Edge(int id_ = -1): id(id_) {}

        template<class C>
        Edge& withUV(C& c, typename C::Value v) {
            c(Arc{2 * id}) = v;
            return *this;
        };

    };

    template<class V>
    using NodeMap = graph_impl::GenMap<Node, V>;

    template<class V>
    using ArcMap = graph_impl::GenMap<Arc, V>;

    Node addNode() {
        int n = (int)nodes_.size();
        nodes_.push_back({});
        return Node{n};
    }

    vector<Node> addNodes(int count) {
        vector<Node> ret(count);
        forn (int, i, count) {
            ret[i] = addNode();
        }
        return ret;
    }

    Edge addEdge(Node u, Node v) {
        assert(valid(u));
        assert(valid(v));
        int n = (int)arcs_.size();
        arcs_.push_back({});
        arcs_[n].to = v.id;
        arcs_[n].nextOut = nodes_[u.id].firstOut;
        nodes_[u.id].firstOut = n;
        arcs_.push_back({});
        arcs_[n + 1].to = u.id;
        arcs_[n + 1].nextOut = nodes_[v.id].firstOut;
        nodes_[v.id].firstOut = n + 1;
        nodes_[u.id].degree++;
        nodes_[v.id].degree++;
        return Edge{n >> 1};
    }

    int nodesCount() const {
        return (int)nodes_.size();
    }

    int arcsCount() const {
        return (int)arcs_.size();
    }

    Arc dual(Arc a) const {
        if (a.id == -1) {
            return a;
        }
        assert(valid(a));
        return Arc{a.id ^ 1};
    }

    Arc firstInArc(Node a) const {
        assert(valid(a));
        return dual(Arc{nodes_[a.id].firstOut});
    }

    Arc firstOutArc(Node a) const {
        assert(valid(a));
        return Arc{nodes_[a.id].firstOut};
    }

    Arc nextInArc(Arc a) const {
        assert(valid(a));
        return dual(Arc{arcs_[dual(a).id].nextOut});
    }

    Arc nextOutArc(Arc a) const {
        assert(valid(a));
        return Arc{arcs_[a.id].nextOut};
    }

    Node from(Arc a) const {
        assert(valid(a));
        return Node{arcs_[dual(a).id].to};
    }

    Node to(Arc a) const {
        assert(valid(a));
        return Node{arcs_[a.id].to};
    }

    template <class T>
    NodeMap<T> nodeMap(T v0 = T{}) const {
        NodeMap<T> ret(v0);
        ret.reserve(nodesCount());
        return ret;
    }

    template <class T>
    ArcMap<T> arcMap(T v0 = T{}) const {
        ArcMap<T> ret(v0);
        ret.reserve(arcsCount());
        return ret;
    }

private:

    static Arc nextInArc_(const Graph& g, Arc a) {
        return g.nextInArc(a);
    }

public:

    graph_impl::GenListIterable<Arc, Graph, &Graph::nextInArc_> inArcs(Node a) const {
        assert(valid(a));
        return graph_impl::GenListIterable<Arc, Graph, &Graph::nextInArc_>(*this, firstInArc(a), Arc{});
    }

    graph_impl::GenRangeIterable<Arc> arcs() const {
        return graph_impl::GenRangeIterable<Arc>(0, arcsCount());
    }

private:
    vector<graph_impl::NodeT> nodes_;
    vector<graph_impl::ArcT> arcs_;
};

// Highest-label variation of Karzanov's algorithm
template <class Cap = int32_t, class Excess = int64_t>
struct BlockingPreflow {
    using Node = Graph::Node;
    using Arc = Graph::Arc;

    BlockingPreflow(const Graph& g_, const Graph::ArcMap<Cap>& cap_): g(g_), cap(cap_) {}

    Excess run(Node s, Node t) {
        residual = cap;
        label = g.nodeMap<int>(g.nodesCount());
        inFlows = g.nodeMap<vector<pair<Cap, Arc>>>();
        excess = g.nodeMap<Excess>();
        excess(s) = numeric_limits<Excess>::max();
        currentArc = g.nodeMap<Arc>();
        while (buildNet(s, t)) {
            int maxDist = label(s);
            int curActive = maxDist;
            priority_queue<pair<int, Node>> inactives;
            while (countActives > 0 || !inactives.empty()) {
                if (countActives == 0) {
                    int l0 = -inactives.top().first;
                    while (!inactives.empty() && -inactives.top().first <= l0 + 1) {
                        auto u = pop(inactives).second;
                        inactive(u) = 1;
                        curActive = label(u) + 1;
                        auto& in = inFlows(u);
                        while (excess(u) > 0) {
                            assert(!in.empty());
                            auto p = pop_back(in);
                            auto a = p.second;
                            auto v = g.to(a);
                            Cap delta = mn(p.first, excess(u));
                            if (excess(v) == 0 && v != s) {
                                activeNodesByLabel[label(v)].push_back(v);
                                countActives++;
                            }
                            excess(u) -= delta;
                            excess(v) += delta;
                            residual(a) -= delta;
                            residual(g.dual(a)) += delta;
                            p.first -= delta;
                            if (p.first > 0) {
                                in.push_back(p);
                            }
                        }
                    }
                }
                if (activeNodesByLabel[curActive].empty()) {
                    curActive--;
                    continue;
                }
                Node u = pop_back(activeNodesByLabel[curActive]);
                if (discharge(u) && u != s) {
                    inactives.emplace(-label(u), u);
                }
            }
        }
        flow = g.arcMap<Cap>();
        for (auto a : g.arcs()) {
            flow(a) = cap(a) - residual(a);
        }
        return value = excess(t);
    }

    bool buildNet(Node s, Node t) {
        label.clear();
        label(t) = 0;
        queue<Node> q;
        q.push(t);
        while (!q.empty()) {
            auto u = pop(q);
            currentArc(u) = g.firstOutArc(u);
            inactive(u) = 0;
            if (u == s) {
                break;
            }
            for (auto a : g.inArcs(u)) {
                auto v = g.from(a);
                if (residual(a) > 0 && label(v) == g.nodesCount()) {
                    label(v) = label(u) + 1;
                    q.push(v);
                }
            }
        }
        if (label(s) == g.nodesCount()) {
            return false;
        }
        inFlows.clear();
        activeNodesByLabel.clear();
        activeNodesByLabel.resize(label(s) + 1);
        activeNodesByLabel[label(s)].push_back(s);
        countActives = 1;
        return true;
    }

    bool push(Arc a, Node u, Node v) {
        if (residual(a) == 0) {
            return false;
        }
        Cap delta = (Cap)mn(excess(u), residual(a));
        if (excess(v) == 0 && label(v) > 0) {
            activeNodesByLabel[label(v)].push_back(v);
            countActives++;
        }
        excess(u) -= delta;
        excess(v) += delta;
        residual(a) -= delta;
        residual(g.dual(a)) += delta;
        inFlows(v).emplace_back(delta, g.dual(a));
        return excess(u) == 0;
    }

    bool discharge(Node u) {
        assert(excess(u) > 0);
        countActives--;
        while (currentArc(u) != Arc{}) {
            auto a = currentArc(u);
            auto v = g.to(a);
            if (label(u) == label(v) + 1 && !inactive(v) && push(a, u, v)) {
                return false;
            }
            currentArc(u) = g.nextOutArc(a);
        }
        return true;
    }

    // outputs
    Excess value;
    Graph::ArcMap<Cap> flow, residual;

private:
    // inputs
    const Graph& g;
    const Graph::ArcMap<Cap>& cap;

    // temp
    Graph::NodeMap<Excess> excess;
    Graph::NodeMap<int> label, inactive;
    Graph::NodeMap<vector<pair<Cap, Arc>>> inFlows;
    Graph::NodeMap<Arc> currentArc;

    vector<vector<Node>> activeNodesByLabel;
    int countActives;
};

// ^^^^^^^^^^^^^^^^^ Library code end

void solve(istream& in, ostream& out) {
    int n = next<int>(in);
    Graph g;
    Graph::Node s = g.addNode(), t = g.addNode();
    auto cap = g.arcMap<int>();
    vector<vector<Graph::Node>> levelsIn, levelsOut;
    levelsIn.push_back(g.addNodes(n));
    levelsOut.push_back(g.addNodes(n));
    forn (int, i, n) {
        g.addEdge(s, levelsIn[0][i]).withUV(cap, 1);
        g.addEdge(levelsOut[0][i], t).withUV(cap, 1);
    }
    for (int l = 1; (1 << l) <= n; ++l) {
        levelsIn.push_back(g.addNodes(n - (1 << l) + 1));
        levelsOut.push_back(g.addNodes(n - (1 << l) + 1));
        forn (int, i, n - (1 << l) + 1) {
            g.addEdge(levelsIn[l - 1][i], levelsIn[l][i]).withUV(cap, n);
            g.addEdge(levelsIn[l - 1][i + (1 << (l - 1))], levelsIn[l][i]).withUV(cap, n);
            g.addEdge(levelsOut[l][i], levelsOut[l - 1][i]).withUV(cap, n);
            g.addEdge(levelsOut[l][i], levelsOut[l - 1][i + (1 << (l - 1))]).withUV(cap, n);
        }
    }
    int qs = next<int>(in);
    using evt = tuple<int, int, pair<int, int>>;
    vector<evt> evs;
    forn (int, q, qs) {
        int x1 = next<int>(in) - 1;
        int y1 = next<int>(in) - 1;
        int x2 = next<int>(in) - 1;
        int y2 = next<int>(in) - 1;
        evs.push_back(evt{x1, +1, {y1, y2 + 1}});
        evs.push_back(evt{x2 + 1, -1, {y1, y2 + 1}});
    }
    evs.push_back(evt{0, -1, {0, n}});
    evs.push_back(evt{n, +1, {0, n}});
    sort(all(evs));
    auto insertRect = [&](int x0, int x1, int y0, int y1) {
        if (x0 == x1 || y0 == y1) {
            return;
        }
        int dx = 0, dy = 0;
        while (x0 + (1 << (dx + 1)) <= x1) {
            ++dx;
        }
        while (y0 + (1 << (dy + 1)) <= y1) {
            ++dy;
        }
        auto u = g.addNode();
        g.addEdge(levelsIn[dx][x0], u).withUV(cap, n);
        g.addEdge(levelsIn[dx][x1 - (1 << dx)], u).withUV(cap, n);
        g.addEdge(u, levelsOut[dy][y0]).withUV(cap, n);
        g.addEdge(u, levelsOut[dy][y1 - (1 << dy)]).withUV(cap, n);
        dbg(x0 + 1, y0 + 1, x1, y1, u.id);
    };
    map<int, pair<int, int>> ranges;
    for (auto& e : evs) {
        int x = get<0>(e);
        auto& range = get<2>(e);
        if (get<1>(e) == -1) {
            ranges[range.first] = {range.second, x};
        } else {
            auto it = ranges.upper_bound(range.first);
            --it;
            pair<int, int> outer;
            outer.first = it->first;
            while (it != ranges.end() && it->first < range.second) {
                insertRect(it->second.second, x, it->first, it->second.first);
                outer.second = it->second.first;
                auto jt = it;
                it++;
                ranges.erase(jt);
            }
            if (outer.first < range.first) {
                ranges[outer.first] = {range.first, x};
            }
            if (range.second < outer.second) {
                ranges[range.second] = {outer.second, x};
            }
        }
    }
    BlockingPreflow<int> f(g, cap);
    out << f.run(s, t) << "\n";
}

int main() {
    solve(cin, cout);
    return 0;
}

#include<bits/stdc++.h>
#define L(i, j, k) for(int i = j, i##E = k; i <= i##E; i++)
#define R(i, j, k) for(int i = j, i##E = k; i >= i##E; i--)
#define ll long long
#define db double
#define pii pair<int, int>
#define mkp make_pair
using namespace std;
const int P = 10009;
const int N = 5e5 + 7;
const int M = 5e6 + 7;
const int inf = 1e9;
int tot, s, t;
int head[N], edge_id = 1;
struct node {
	int to, next, val;
} e[M << 1];
void add_edge(int u, int v, int val) {
	++edge_id, e[edge_id].next = head[u], e[edge_id].to = v, e[edge_id].val = val, head[u] = edge_id;
}
void add(int u, int v, int val) {
	// cout << u << " " << v << " " << val << endl;
	add_edge(u, v, val), add_edge(v, u, 0);
}
int dis[N], cur[N];
bool bfs() {
	fill(dis + 1, dis + tot + 1, inf);
	copy(head + 1, head + tot + 1, cur + 1);
	queue<int> q; q.push(s), dis[s] = 0;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		for(int i = head[u]; i; i = e[i].next) {
			int v = e[i].to;
			if(e[i].val && dis[v] >= inf) dis[v] = dis[u] + 1, q.push(v);
		}
	}
	return dis[t] < inf;
}
int dfs(int x, int flow) {
	if(x == t) return flow;
	int now = flow;
	for(int i = cur[x]; i; i = e[i].next) {
		int v = e[i].to;
		cur[x] = i;
		if(!e[i].val || dis[v] != dis[x] + 1) continue;
		int fl = dfs(v, min(now, e[i].val));
		now -= fl, e[i].val -= fl, e[i ^ 1].val += fl;
		if(!now) break;
	}
	return flow - now;
}
int dinic() {
	int res = 0;
	while(bfs()) res += dfs(s, inf);
	return res;
}
int n, m, now;
int p[N], hd[N], ch[N][2], Val[N];
vector<pii> vea[P];
vector<pii> veb[P];
void build(int id, int L, int R) {
	Val[id] = inf;
	if(L == R) return add(id, L, 1), void();
	int mid = (L + R) / 2;
	ch[id][0] = ++tot, add(id, ch[id][0], inf), build(ch[id][0], L, mid);
	ch[id][1] = ++tot, add(id, ch[id][1], inf), build(ch[id][1], mid + 1, R);
}
void Add(int &id, int fid, int las, int L, int R, int l, int r, int val) {
	id = ++tot;
	int mid = (L + R) / 2;
	if(l <= L && R <= r) {
		// cout << L << " " << R << " : " << val << endl;
		if(l == 1 && r == n) {
			Add(ch[id][0], ch[fid][0], id, L, mid, l, mid, val);
			Add(ch[id][1], ch[fid][1], id, mid + 1, R, mid + 1, r, val);
			return;
		}
		if(las) add(las, id, Val[id] = val);
		if(L ^ R) {
			ch[id][0] = ch[fid][0], add(id, ch[id][0], Val[ch[id][0]]);
			ch[id][1] = ch[fid][1], add(id, ch[id][1], Val[ch[id][1]]);
		}
		else add(id, L, 1);
		return;
	}
	if(las) add(las, id, Val[id] = Val[fid]);
	if(l <= mid) Add(ch[id][0], ch[fid][0], id, L, mid, l, r, val);
	else ch[id][0] = ch[fid][0], add(id, ch[id][0], Val[ch[id][0]]);
	if(r > mid) Add(ch[id][1], ch[fid][1], id, mid + 1, R, l, r, val);
	else ch[id][1] = ch[fid][1], add(id, ch[id][1], Val[ch[id][1]]);
}
int main() {
	scanf("%d%d", &n, &m), tot = n * 2, s = ++tot, t = ++tot;
	if(n == 1) return printf("%d\n", 1 - m), 0;
	L(i, 1, m) {
		int l1, l2, r1, r2; scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
		vea[l1].push_back(mkp(r1, r2));
		veb[l2].push_back(mkp(r1, r2));
	}
	hd[0] = ++tot, build(hd[0], 1, n);
	L(i, 1, n) add(s, i + n, 1);
	L(i, 1, n) add(i, t, 1);
	// Add(hd[now + 1], hd[now], 0, 1, n, 1, n, 0), ++now;
	// Add(hd[now + 1], hd[now], 0, 1, n, 1, n, inf), ++now;
	L(i, 1, n) {
		for(pii x : vea[i]) Add(hd[now + 1], hd[now], 0, 1, n, x.first, x.second, 0), ++now;
		add(i + n, hd[now], 1);
		for(pii x : veb[i]) Add(hd[now + 1], hd[now], 0, 1, n, x.first, x.second, inf), ++now;
	}
	printf("%d\n", dinic());
	return 0;
}

// vvvvvvvvvvvvvvvvv Library code start

#define NDEBUG
NDEBUG

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <memory>
#include <queue>
#include <random>

#define forn(t, i, n) for (t i = 0; i < (n); ++i)

#define all(c) c.begin(), c.end()

using namespace std;

/// caide keep
bool __hack = std::ios::sync_with_stdio(false);
/// caide keep
auto __hack1 = cin.tie(nullptr);

template<class T>
inline T mn(T arg) {
    return arg;
}

template<class T, class... Ts>
inline typename common_type<T, Ts...>::type mn(T arg, Ts... args) {
    auto arg1 = mn(args...);
    return arg < arg1 ? arg : arg1;
}

template<class T>
inline T mx(T arg) {
    return arg;
}

template<class T, class... Args>
inline bool rmn(T &to, Args... args) {
    auto v = mn(args...);
    if (to > v) {
        to = v;
        return true;
    }
    return false;
}

template<class T, class... Args>
inline bool rmx(T &to, Args... args) {
    auto v = mx(args...);
    if (to < v) {
        to = v;
        return true;
    }
    return false;
}

template <class T>
T pop_back(vector<T>& v) {
    T ret = v.back();
    v.pop_back();
    return ret;
}

template <class T>
T pop(queue<T>& v) {
    T ret = v.front();
    v.pop();
    return ret;
}

// Section with adoption of array and vector algorithms.

// 32 bit ints (sum LSB + MSB == 32)

// TODO: Section with some container algorithms

namespace template_util {

    constexpr int bytecount(uint64_t x) {
        return x ? 1 + bytecount(x >> 8) : 0;
    }

    /// caide keep
    template<int N>
    struct bytetype {

    };

    /// caide keep
    template<>
    struct bytetype<4> {

    };

    /// caide keep
    template<>
    struct bytetype<3> {

    };

    /// caide keep
    template<>
    struct bytetype<2> {

    };

    /// caide keep
    template<>
    struct bytetype<1> {

    };

    /// caide keep
    template<>
    struct bytetype<0> {

    };

    /// caide keep
    template<uint64_t N>
    struct minimal_uint : bytetype<bytecount(N)> {
    };
}

template<class T>
T next(istream& in) {
    T ret;
    in >> ret;
    return ret;
}

#define dbg(...) ;

//TODO migrate iterators into iterator_utils
namespace graph_impl {

    template<class T, class G, T next(const G&, T)>
    struct GenListIterator {
        GenListIterator(const G& g_, T t_): g(g_), t(t_) {}

        T operator*() const {
            return t;
        }

        GenListIterator& operator++() {
            t = next(g, t);
            return *this;
        }

        bool operator!=(const GenListIterator& o) const {
            assert(&g == &o.g);
            return t != o.t;
        }
    private:
        const G& g;
        T t;
    };

    template<class T, class G, T next(const G&, T)>
    struct GenListIterable {
        GenListIterable(const G& g_, T from_, T to_): g(g_), from(from_), to(to_) {}

        GenListIterator<T, G, next> begin() {
            return {g, from};
        };

        GenListIterator<T, G, next> end() {
            return {g, to};
        };
    private:
        const G& g;
        T from, to;
    };

    template<class T>
    struct GenRangeIterator {
        explicit GenRangeIterator(int at_): at(at_) {}

        T operator*() const {
            return T{at};
        }

        GenRangeIterator& operator++() {
            at++;
            return *this;
        }

        bool operator!=(const GenRangeIterator& o) const {
            return at != o.at;
        }
    private:
        int at;
    };

    template<class T>
    struct GenRangeIterable {
        GenRangeIterable(int from_, int to_):  from(from_), to(to_) {}

        GenRangeIterator<T> begin() {
            return GenRangeIterator<T>{from};
        };

        GenRangeIterator<T> end() {
            return GenRangeIterator<T>{to};
        };
    private:
        int from, to;
    };

    template<class K, class V>
    struct GenMap {
        using Value = V;

        explicit GenMap(V v_ = V{}): v(v_) {
        }

        GenMap& reserve(int n) {
            impl_.reserve(n);
            return *this;
        }

        GenMap& clear() {
            impl_.clear();
            return *this;
        }

        V& operator()(K k) {
            if (impl_.size() <= k.id) {
                impl_.resize(k.id + 1, v);
            }
            return impl_[k.id];
        }

        const V& operator()(K k) const {
            if (impl_.size() <= k.id) {
                return v;
            }
            return impl_[k.id];
        }
        vector<V> impl_;
    private:
        V v;
    };

    struct NodeT {
        int firstOut = -1;
        int degree = 0;
    };

    struct ArcT {
        int to, nextOut;
    };
}

struct Graph {
    struct Node {
        int id;
        explicit Node(int id_ = -1): id(id_) {}

        bool operator!=(const Node o) const {return id != o.id;}

    };

    struct Arc {
        int id;
        explicit Arc(int id_ = -1): id(id_) {}
        bool operator==(const Arc o) const {return id == o.id;}
        bool operator!=(const Arc o) const {return id != o.id;}

    };

    struct Edge {
        int id;
        explicit Edge(int id_ = -1): id(id_) {}

        template<class C>
        Edge& withUV(C& c, typename C::Value v) {
            c(Arc{2 * id}) = v;
            return *this;
        };

    };

    template<class V>
    using NodeMap = graph_impl::GenMap<Node, V>;

    template<class V>
    using ArcMap = graph_impl::GenMap<Arc, V>;

    Node addNode() {
        int n = (int)nodes_.size();
        nodes_.push_back({});
        return Node{n};
    }

    vector<Node> addNodes(int count) {
        vector<Node> ret(count);
        forn (int, i, count) {
            ret[i] = addNode();
        }
        return ret;
    }

    Edge addEdge(Node u, Node v) {
        assert(valid(u));
        assert(valid(v));
        int n = (int)arcs_.size();
        arcs_.push_back({});
        arcs_[n].to = v.id;
        arcs_[n].nextOut = nodes_[u.id].firstOut;
        nodes_[u.id].firstOut = n;
        arcs_.push_back({});
        arcs_[n + 1].to = u.id;
        arcs_[n + 1].nextOut = nodes_[v.id].firstOut;
        nodes_[v.id].firstOut = n + 1;
        nodes_[u.id].degree++;
        nodes_[v.id].degree++;
        return Edge{n >> 1};
    }

    int nodesCount() const {
        return (int)nodes_.size();
    }

    int arcsCount() const {
        return (int)arcs_.size();
    }

    int edgesCount() const {
        return (int)arcs_.size() / 2;
    }

    Arc dual(Arc a) const {
        if (a.id == -1) {
            return a;
        }
        assert(valid(a));
        return Arc{a.id ^ 1};
    }

    Arc firstInArc(Node a) const {
        assert(valid(a));
        return dual(Arc{nodes_[a.id].firstOut});
    }

    Arc firstOutArc(Node a) const {
        assert(valid(a));
        return Arc{nodes_[a.id].firstOut};
    }

    Arc nextInArc(Arc a) const {
        assert(valid(a));
        return dual(Arc{arcs_[dual(a).id].nextOut});
    }

    Arc nextOutArc(Arc a) const {
        assert(valid(a));
        return Arc{arcs_[a.id].nextOut};
    }

    Node from(Arc a) const {
        assert(valid(a));
        return Node{arcs_[dual(a).id].to};
    }

    Node to(Arc a) const {
        assert(valid(a));
        return Node{arcs_[a.id].to};
    }

    template <class T>
    NodeMap<T> nodeMap(T v0 = T{}) const {
        NodeMap<T> ret(v0);
        ret.reserve(nodesCount());
        return ret;
    }

    template <class T>
    ArcMap<T> arcMap(T v0 = T{}) const {
        ArcMap<T> ret(v0);
        ret.reserve(arcsCount());
        return ret;
    }

private:

    static Arc nextInArc_(const Graph& g, Arc a) {
        return g.nextInArc(a);
    }

    static Arc nextOutArc_(const Graph& g, Arc a) {
        return g.nextOutArc(a);
    }

public:

    graph_impl::GenListIterable<Arc, Graph, &Graph::nextInArc_> inArcs(Node a) const {
        assert(valid(a));
        return graph_impl::GenListIterable<Arc, Graph, &Graph::nextInArc_>(*this, firstInArc(a), Arc{});
    }

    graph_impl::GenListIterable<Arc, Graph, &Graph::nextOutArc_> outArcs(Node a) const {
        assert(valid(a));
        return graph_impl::GenListIterable<Arc, Graph, &Graph::nextOutArc_>(*this, firstOutArc(a), Arc{});
    }

    graph_impl::GenRangeIterable<Node> nodes() const {
        return graph_impl::GenRangeIterable<Node>(0, nodesCount());
    }

    graph_impl::GenRangeIterable<Arc> arcs() const {
        return graph_impl::GenRangeIterable<Arc>(0, arcsCount());
    }

private:
    vector<graph_impl::NodeT> nodes_;
    vector<graph_impl::ArcT> arcs_;
};

// TODO: gap heuristic & relabel sorting heuristic
// https://www.ads.tuwien.ac.at/teaching/archiv/praktika/CherkasskyGoldberg-1995-MaxFlow.pdf
template <class Cap>
struct PushRelabel {
    using Node = Graph::Node;
    using Arc = Graph::Arc;

    PushRelabel(const Graph& g_, const Graph::ArcMap<Cap>& cap_): g(g_), cap(cap_) {}

    Cap run(Node s, Node t) {
        flow = g.arcMap<Cap>();
        residual = cap;
        label = g.nodeMap<int>(g.nodesCount());
        excess = g.nodeMap<Cap>();
        excess(s) = numeric_limits<Cap>::max();
        currentArc = g.nodeMap<Arc>();
        for (auto v : g.nodes()) {
            currentArc(v) = g.firstOutArc(v);
        }
        fullRelabel(t);
        while (currentActiveLabel > 0) {
            if (activeNodesByLabel[currentActiveLabel].empty()) {
                currentActiveLabel--;
                continue;
            }
            auto u = pop_back(activeNodesByLabel[currentActiveLabel]);
            discharge(u);
            currentActiveLabel = label(u);
            if (stepsCount >= g.edgesCount()) {
                fullRelabel(t);
                stepsCount = 0;
            }
        }
        //TODO
        for (auto a : g.arcs()) {
            flow(a) = cap(a) - residual(a);
            if (flow(a) > 0) {
                dbg(g.from(a).id, g.to(a).id, flow(a));
            }
        }
        return value = excess(t);
    }

    void fullRelabel(Node t) {
        label.clear();
        label(t) = 0;
        queue<Node> q;
        q.push(t);
        while (!q.empty()) {
            auto u = pop(q);
            for (auto a : g.inArcs(u)) {
                auto v = g.from(a);
                if (residual(a) > 0 && label(v) == g.nodesCount()) {
                    label(v) = label(u) + 1;
                    q.push(v);
                }
            }
        }
        activeNodesByLabel.clear();
        activeNodesByLabel.resize(g.nodesCount() + 1);
        currentActiveLabel = 0;
        for (auto u : g.nodes()) {
            if (u != t && excess(u) > 0 && label(u) < g.nodesCount()) {
                activeNodesByLabel[label(u)].push_back(u);
                rmx(currentActiveLabel, label(u));
            }
        }
    }

    void relabel(Node u) {
        int before = label(u);
        label(u) = g.nodesCount();
        for (auto a : g.outArcs(u)) {
            if (residual(a) > 0) {
                rmn(label(u), label(g.to(a)) + 1);
            }
            stepsCount++;
        }
        if (label(u) <= before) {
            dbg("wtf");
        }
    }

    void discharge(Node u) {
        int highestLabel = label(u);
        while (label(u) < g.nodesCount()) {
            stepsCount++;
            auto a = currentArc(u);
            auto v = g.to(a);
            // push(a)
            if (residual(a) > 0 && label(u) == label(v) + 1) {
                int delta = mn(excess(u), residual(a));
                if (excess(v) == 0) {
                    activeNodesByLabel[label(v)].push_back(v);
                }
                excess(u) -= delta;
                excess(v) += delta;
                residual(a) -= delta;
                residual(g.dual(a)) += delta;
                if (excess(u) == 0) {
                    break;
                }
            }
            currentArc(u) = g.nextOutArc(a);
            if (currentArc(u) == Arc{}) {
                relabel(u);
                currentArc(u) = g.firstOutArc(u);
            }
        }
    }

    // outputs
    int value;
    Graph::ArcMap<Cap> flow, residual;

private:
    // inputs
    const Graph& g;
    const Graph::ArcMap<Cap>& cap;

    // temp
    Graph::NodeMap<Cap> excess;
    Graph::NodeMap<int> label;
    Graph::NodeMap<Arc> currentArc;
    int stepsCount = 0;

    vector<vector<Node>> activeNodesByLabel;
    int currentActiveLabel;
};

// ^^^^^^^^^^^^^^^^^ Library code end

void solve(istream& in, ostream& out) {
    int n = next<int>(in);
    Graph g;
    Graph::Node s = g.addNode(), t = g.addNode();
    auto cap = g.arcMap<int>();
    vector<vector<Graph::Node>> levelsIn, levelsOut;
    levelsIn.push_back(g.addNodes(n));
    levelsOut.push_back(g.addNodes(n));
    forn (int, i, n) {
        g.addEdge(s, levelsIn[0][i]).withUV(cap, 1);
        g.addEdge(levelsOut[0][i], t).withUV(cap, 1);
    }
    for (int l = 1; (1 << l) <= n; ++l) {
        levelsIn.push_back(g.addNodes(n - (1 << l) + 1));
        levelsOut.push_back(g.addNodes(n - (1 << l) + 1));
        forn (int, i, n - (1 << l) + 1) {
            g.addEdge(levelsIn[l - 1][i], levelsIn[l][i]).withUV(cap, n);
            g.addEdge(levelsIn[l - 1][i + (1 << (l - 1))], levelsIn[l][i]).withUV(cap, n);
            g.addEdge(levelsOut[l][i], levelsOut[l - 1][i]).withUV(cap, n);
            g.addEdge(levelsOut[l][i], levelsOut[l - 1][i + (1 << (l - 1))]).withUV(cap, n);
        }
    }
    int qs = next<int>(in);
    using evt = tuple<int, int, pair<int, int>>;
    vector<evt> evs;
    forn (int, q, qs) {
        int x1 = next<int>(in) - 1;
        int y1 = next<int>(in) - 1;
        int x2 = next<int>(in) - 1;
        int y2 = next<int>(in) - 1;
        evs.push_back(evt{x1, +1, {y1, y2 + 1}});
        evs.push_back(evt{x2 + 1, -1, {y1, y2 + 1}});
    }
    evs.push_back(evt{0, -1, {0, n}});
    evs.push_back(evt{n, +1, {0, n}});
    sort(all(evs));
    auto insertRect = [&](int x0, int x1, int y0, int y1) {
        if (x0 == x1 || y0 == y1) {
            return;
        }
        int dx = 0, dy = 0;
        while (x0 + (1 << (dx + 1)) <= x1) {
            ++dx;
        }
        while (y0 + (1 << (dy + 1)) <= y1) {
            ++dy;
        }
        auto u = g.addNode();
        g.addEdge(levelsIn[dx][x0], u).withUV(cap, n);
        g.addEdge(levelsIn[dx][x1 - (1 << dx)], u).withUV(cap, n);
        g.addEdge(u, levelsOut[dy][y0]).withUV(cap, n);
        g.addEdge(u, levelsOut[dy][y1 - (1 << dy)]).withUV(cap, n);
        dbg(x0 + 1, y0 + 1, x1, y1, u.id);
    };
    map<int, pair<int, int>> ranges;
    for (auto& e : evs) {
        int x = get<0>(e);
        auto& range = get<2>(e);
        if (get<1>(e) == -1) {
            ranges[range.first] = {range.second, x};
        } else {
            auto it = ranges.upper_bound(range.first);
            --it;
            pair<int, int> outer;
            outer.first = it->first;
            while (it != ranges.end() && it->first < range.second) {
                insertRect(it->second.second, x, it->first, it->second.first);
                outer.second = it->second.first;
                auto jt = it;
                it++;
                ranges.erase(jt);
            }
            if (outer.first < range.first) {
                ranges[outer.first] = {range.first, x};
            }
            if (range.second < outer.second) {
                ranges[range.second] = {outer.second, x};
            }
        }
    }
    PushRelabel<int> f(g, cap);
    out << f.run(s, t) << "\n";
}

int main() {
    solve(cin, cout);
    return 0;
}

// eddy1021
#pragma GCC optimize("O3")
#include <bits/stdc++.h>
using namespace std;
typedef double D;
typedef long double LD;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
#define mod9 1000000009LL
#define mod7 1000000007LL
#define INF16 10000000000000000LL
#define eps 1e-9
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#ifndef ONLINE_JUDGE
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
inline LL getint(){
  LL _x=0,_tmp=1; char _tc=getchar();
  while( (_tc<'0'||_tc>'9')&&_tc!='-' ) _tc=getchar();
  if( _tc == '-' ) _tc=getchar() , _tmp = -1;
  while(_tc>='0'&&_tc<='9') _x*=10,_x+=(_tc-'0'),_tc=getchar();
  return _x*_tmp;
}
inline LL add( LL _x , LL _y , LL _mod = mod7 ){
  _x += _y;
  return _x >= _mod ? _x - _mod : _x;
}
inline LL sub( LL _x , LL _y , LL _mod = mod7 ){
  _x -= _y;
  return _x < 0 ? _x + _mod : _x;
}
inline LL mul( LL _x , LL _y , LL _mod = mod7 ){
  _x *= _y;
  return _x >= _mod ? _x % _mod : _x;
}
LL mypow( LL _a , LL _x , LL _mod ){
  if( _x == 0 ) return 1LL;
  LL _ret = mypow( mul( _a , _a , _mod ) , _x >> 1 , _mod );
  if( _x & 1 ) _ret = mul( _ret , _a , _mod );
  return _ret;
}
LL mymul( LL _a , LL _x , LL _mod ){
  if( _x == 0 ) return 0LL;
  LL _ret = mymul( add( _a , _a , _mod ) , _x >> 1 , _mod );
  if( _x & 1 ) _ret = add( _ret , _a , _mod );
  return _ret;
}
inline bool equal( D _x ,  D _y ){
  return _x > _y - eps && _x < _y + eps;
}
#define Bye exit(0)
int __ = 1 , _cs;
/*********default*********/
#define x1 sdfjklsj
#define x2 sdfjklsk
#define y1 sdfjklsl
#define y2 sdfjklsm
#define N 10101
struct Maxflow {
  static const int MAXV = 100010;
  static const int INF  = 1000000;
  struct Edge {
    int v, c, r;
    Edge(int _v, int _c, int _r):
      v(_v), c(_c), r(_r) {}
  };
  int s, t;
  vector<Edge> G[MAXV*2];
  int iter[MAXV*2], d[MAXV*2], gap[MAXV*2], tot;
  void init(int x) {
    tot = x+2;
    s = x+1, t = x+2;
    for(int i = 0; i <= tot; i++) {
      G[i].clear();
      iter[i] = d[i] = gap[i] = 0;
    }
  }
  void addEdge(int u, int v, int c) {
    //printf( "%d %d -> %d\n" , u , v , c );
    G[u].push_back(Edge(v, c, SZ(G[v]) ));
    G[v].push_back(Edge(u, 0, SZ(G[u]) - 1));
  }
  int dfs(int p, int flow) {
    if(p == t) return flow;
    int ret = 0;
    for(int &i = iter[p]; i < SZ(G[p]); i++) {
      Edge &e = G[p][i];
      if(e.c > 0 && d[p] == d[e.v]+1) {
        int f = dfs(e.v, min(flow, e.c));
        ret += f;
        flow -= f;
        e.c -= f;
        G[e.v][e.r].c += f;
        if (flow == 0) {
            break;
        }
      }
    }
    if (ret > 0) {
        return ret;
    }
    if( (--gap[d[p]]) == 0) d[s] = tot;
    else {
      d[p]++;
      iter[p] = 0;
      ++gap[d[p]];
    }
    return 0;
  }
  int solve() {
    //puts("MF");
    int res = 0;
    gap[0] = tot;
    for(res = 0; d[s] < tot; res += dfs(s, INF));
    return res;
  }
} flow;
int tg[ N << 2 ];
int mn[ N << 2 ] , mx[ N << 2 ];
#define L(X) (X<<1)
#define R(X) (1+(X<<1))
#define mid ((l+r)>>1)
void push( int no , int l , int r ){
  if( !tg[ no ] ) return;
  tg[ L( no ) ] += tg[ no ];
  mn[ L( no ) ] += tg[ no ];
  mx[ L( no ) ] += tg[ no ];
  tg[ R( no ) ] += tg[ no ];
  mn[ R( no ) ] += tg[ no ];
  mx[ R( no ) ] += tg[ no ];
  tg[ no ] = 0;
}
void modify( int no , int l , int r , int ql , int qr , int qd ){
  if( r < ql or l > qr ) return;
  if( ql <= l and r <= qr ){
    tg[ no ] += qd;
    mn[ no ] += qd;
    mx[ no ] += qd;
    return;
  }
  push( no , l , r );
  modify( L( no ) , l , mid , ql , qr , qd );
  modify( R( no ) , mid + 1 , r , ql , qr , qd );
  mn[ no ] = min( mn[ L( no ) ] , mn[ R( no ) ] );
  mx[ no ] = max( mx[ L( no ) ] , mx[ R( no ) ] );
}
int id[ N << 2 ] , stmp;
void mark( int no , int l , int r ){
  id[ no ] = ++ stmp;
  if( l == r ) return;
  mark( L( no ) , l , mid );
  mark( R( no ) , mid + 1 , r );
}
void build_edge( int no , int l , int r ){
  if( l == r ){
    flow.addEdge( flow.s , id[ no ] , 1 );
    flow.addEdge( id[ no ] + stmp , flow.t , 1 );
    return;
  }
  flow.addEdge( id[ L( no ) ] , id[ no ] , N );
  flow.addEdge( id[ R( no ) ] , id[ no ] , N );
  flow.addEdge( id[ no ] + stmp , id[ L( no ) ] + stmp , N );
  flow.addEdge( id[ no ] + stmp , id[ R( no ) ] + stmp , N );
  build_edge( L( no ) , l , mid );
  build_edge( R( no ) , mid + 1 , r );
}
void build(){

}
int n , m;
vector< pair< pair<int,int> , int > > dlt[ N ];
vector<int> candx;
void init(){
  n = getint();
  mark( 1 , 1 , n );
  flow.init( stmp + stmp );
  build_edge( 1 , 1 , n );
  m = getint(); while( m -- ){
    int x1 = getint();
    int y1 = getint();
    int x2 = getint();
    int y2 = getint();
    dlt[ x1     ].push_back( { { y1 , y2 } , +1 } );
    dlt[ x2 + 1 ].push_back( { { y1 , y2 } , -1 } );
    candx.push_back( x1 );
    candx.push_back( x2 + 1 );
  }
}
vector<int> lid;
void find_id( int no , int l , int r , int ql , int qr ){
  if( r < ql or l > qr ) return;
  if( ql <= l and r <= qr ){
    lid.push_back( id[ no ] );
    return;
  }
  find_id( L( no ) , l , mid , ql , qr );
  find_id( R( no ) , mid + 1 , r , ql , qr );
}
void gogo( int no , int l , int r ){
  if( mn[ no ] > 0 ) return;
  if( mx[ no ] == 0 ){
    for( auto ii : lid )
      flow.addEdge( ii , id[ no ] + stmp , N );
    return;
  }
  if( l == r ) return;
  push( no , l , r );
  gogo( L( no ) , l , mid );
  gogo( R( no ) , mid + 1 , r );
}
void solve(){
  if( candx.empty() ){
    printf( "%d\n" , n );
    Bye;
  }
  sort( candx.begin() , candx.end() );
  candx.resize( unique( candx.begin() , candx.end() ) - candx.begin() );
  if( candx[ 0 ] > 1 ){
    lid.clear();
    find_id( 1 , 1 , n , 1 , candx[ 0 ] - 1 );
    gogo( 1 , 1 , n );
  }
  for( size_t _ = 0 ; _ < candx.size() ; _ ++ ){
    int x = candx[ _ ];
    int til = _ + 1 < candx.size() ? candx[ _ + 1 ] - 1 : n;
    for( auto i : dlt[ x ] )
      modify( 1 , 1 , n , i.first.first , i.first.second , i.second );
    lid.clear();
    find_id( 1 , 1 , n , x , til );
    gogo( 1 , 1 , n );
  }
  printf( "%d\n" , flow.solve() );
}
int main(){
  build();
  //__ = getint();
  while( __ -- ){
    init();
    solve();
  }
}

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define uint unsigned
#define pii pair<int,int>
#define pll pair<ll,ll>
#define IT iterator
#define PB push_back
#define fi first
#define se second
#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);i++)
#define Rep(i,j,k) for (int i=(int)(j);i>=(int)(k);i--)
#define CLR(a,v) memset(a,v,sizeof(a));
#define CPY(a,b) memcpy(a,b,sizeof(a));
#define debug puts("wzpakking")
#define y1 ysghysgsygsh
using namespace std;

const int N=10005;
const int M=1000005;
struct edge{
	int to,next,f;
}e[M*5];
vector<pii> v1[N],v2[N];
int n,Q,rt,head[M],tot=1,nd,S,T;
int ls[M],rs[M],q[M],dis[M],ans;
void add(int,int,int);
void build(int &k,int l,int r){
	if (l==r) return k=n+l,void(0);
	int mid=(l+r)/2;
	k=++nd;
	build(ls[k],l,mid);
	build(rs[k],mid+1,r);
	add(k,ls[k],1<<30);
	add(k,rs[k],1<<30);
}
void insert(int &nk,int k,int p,int l,int r,int x,int y){
	if (x<=l&&r<=y) return nk=p,void(0);
	int mid=(l+r)/2;
	nk=++nd; ls[nk]=ls[k]; rs[nk]=rs[k];
	if (x<=mid) insert(ls[nk],ls[k],ls[p],l,mid,x,y);
	if (y>mid) insert(rs[nk],rs[k],rs[p],mid+1,r,x,y);
	add(nk,ls[nk],1<<30);
	add(nk,rs[nk],1<<30);
}

void add(int x,int y,int v){
	e[++tot]=(edge){y,head[x],v};
	head[x]=tot;
	e[++tot]=(edge){x,head[y],0};
	head[y]=tot;
}
bool bfs(){
	For(i,1,nd) dis[i]=-1;
	int h=0,t=1; q[1]=S; dis[S]=0;
	while (h!=t){
		int x=q[++h];
		for (int i=head[x];i;i=e[i].next)
			if (dis[e[i].to]==-1&&e[i].f){
				dis[e[i].to]=dis[x]+1;
				if (e[i].to==T) return 1;
				q[++t]=e[i].to;
			}
	}
	return 0;
}
int dfs(int x,int flow){
	if (x==T) return flow;
	int k,rest=flow;
	for (int i=head[x];i&&rest;i=e[i].next)
		if (dis[e[i].to]==dis[x]+1&&e[i].f){
			k=dfs(e[i].to,min(rest,e[i].f));
			e[i].f-=k; e[i^1].f+=k; rest-=k;
		}
	if (rest) dis[x]=-1;
	return flow-rest;
}
int main(){
	//freopen("1.in","r",stdin);
	scanf("%d",&n);
	nd=2*n; S=++nd; T=++nd;
	For(i,1,n) add(S,i,1),add(i+n,T,1);
	build(rt,1,n);
	scanf("%d",&Q);
	For(i,1,Q){
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		v1[x1].PB(pii(y1,y2));
		v2[x2+1].PB(pii(y1,y2));
	}
	int ini=rt;
	For(i,1,n){
		for (auto j:v2[i]) insert(rt,rt,ini,1,n,j.fi,j.se);
		for (auto j:v1[i]) insert(rt,rt,0,1,n,j.fi,j.se);
		add(i,rt,1);
	}
	int ans=0;
	for (;bfs();ans+=dfs(S,1<<30));
	printf("%d\n",ans);
}

// vvvvvvvvvvvvvvvvv Library code start

#define NDEBUG
NDEBUG

#include <algorithm>
#include <array>
#include <bitset>
#include <cassert>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <memory>
#include <queue>
#include <random>

#define forn(t, i, n) for (t i = 0; i < (n); ++i)

#define all(c) c.begin(), c.end()

using namespace std;

/// caide keep
bool __hack = std::ios::sync_with_stdio(false);
/// caide keep
auto __hack1 = cin.tie(nullptr);

template<class T>
inline T mn(T arg) {
    return arg;
}

template<class T, class... Ts>
inline typename common_type<T, Ts...>::type mn(T arg, Ts... args) {
    auto arg1 = mn(args...);
    return arg < arg1 ? arg : arg1;
}

template<class T>
inline T mx(T arg) {
    return arg;
}

template<class T, class... Args>
inline bool rmn(T &to, Args... args) {
    auto v = mn(args...);
    if (to > v) {
        to = v;
        return true;
    }
    return false;
}

template<class T, class... Args>
inline bool rmx(T &to, Args... args) {
    auto v = mx(args...);
    if (to < v) {
        to = v;
        return true;
    }
    return false;
}

template <class T>
T pop_back(vector<T>& v) {
    T ret = v.back();
    v.pop_back();
    return ret;
}

template <class T>
T pop(queue<T>& v) {
    T ret = v.front();
    v.pop();
    return ret;
}

// Section with adoption of array and vector algorithms.

// 32 bit ints (sum LSB + MSB == 32)

// TODO: Section with some container algorithms

namespace template_util {

    constexpr int bytecount(uint64_t x) {
        return x ? 1 + bytecount(x >> 8) : 0;
    }

    /// caide keep
    template<int N>
    struct bytetype {

    };

    /// caide keep
    template<>
    struct bytetype<4> {

    };

    /// caide keep
    template<>
    struct bytetype<3> {

    };

    /// caide keep
    template<>
    struct bytetype<2> {

    };

    /// caide keep
    template<>
    struct bytetype<1> {

    };

    /// caide keep
    template<>
    struct bytetype<0> {

    };

    /// caide keep
    template<uint64_t N>
    struct minimal_uint : bytetype<bytecount(N)> {
    };
}

template<class T>
T next(istream& in) {
    T ret;
    in >> ret;
    return ret;
}

#define dbg(...) ;

//TODO migrate iterators into iterator_utils
namespace graph_impl {

    template<class T, class G, T next(const G&, T)>
    struct GenListIterator {
        GenListIterator(const G& g_, T t_): g(g_), t(t_) {}

        T operator*() const {
            return t;
        }

        GenListIterator& operator++() {
            t = next(g, t);
            return *this;
        }

        bool operator!=(const GenListIterator& o) const {
            assert(&g == &o.g);
            return t != o.t;
        }
    private:
        const G& g;
        T t;
    };

    template<class T, class G, T next(const G&, T)>
    struct GenListIterable {
        GenListIterable(const G& g_, T from_, T to_): g(g_), from(from_), to(to_) {}

        GenListIterator<T, G, next> begin() {
            return {g, from};
        };

        GenListIterator<T, G, next> end() {
            return {g, to};
        };
    private:
        const G& g;
        T from, to;
    };

    template<class T>
    struct GenRangeIterator {
        explicit GenRangeIterator(int at_): at(at_) {}

        T operator*() const {
            return T{at};
        }

        GenRangeIterator& operator++() {
            at++;
            return *this;
        }

        bool operator!=(const GenRangeIterator& o) const {
            return at != o.at;
        }
    private:
        int at;
    };

    template<class T>
    struct GenRangeIterable {
        GenRangeIterable(int from_, int to_):  from(from_), to(to_) {}

        GenRangeIterator<T> begin() {
            return GenRangeIterator<T>{from};
        };

        GenRangeIterator<T> end() {
            return GenRangeIterator<T>{to};
        };
    private:
        int from, to;
    };

    template<class K, class V>
    struct GenMap {
        using Value = V;

        explicit GenMap(V v_ = V{}): v(v_) {
        }

        GenMap& reserve(int n) {
            impl_.reserve(n);
            return *this;
        }

        GenMap& clear() {
            impl_.clear();
            return *this;
        }

        V& operator()(K k) {
            if (impl_.size() <= k.id) {
                impl_.resize(k.id + 1, v);
            }
            return impl_[k.id];
        }

        const V& operator()(K k) const {
            if (impl_.size() <= k.id) {
                return v;
            }
            return impl_[k.id];
        }
        vector<V> impl_;
    private:
        V v;
    };

    struct NodeT {
        int firstOut = -1;
        int degree = 0;
    };

    struct ArcT {
        int to, nextOut;
    };
}

struct Graph {
    struct Node {
        int id;
        explicit Node(int id_ = -1): id(id_) {}

        bool operator!=(const Node o) const {return id != o.id;}

    };

    struct Arc {
        int id;
        explicit Arc(int id_ = -1): id(id_) {}
        bool operator==(const Arc o) const {return id == o.id;}
        bool operator!=(const Arc o) const {return id != o.id;}

    };

    struct Edge {
        int id;
        explicit Edge(int id_ = -1): id(id_) {}

        template<class C>
        Edge& withUV(C& c, typename C::Value v) {
            c(Arc{2 * id}) = v;
            return *this;
        };

    };

    template<class V>
    using NodeMap = graph_impl::GenMap<Node, V>;

    template<class V>
    using ArcMap = graph_impl::GenMap<Arc, V>;

    Node addNode() {
        int n = (int)nodes_.size();
        nodes_.push_back({});
        return Node{n};
    }

    vector<Node> addNodes(int count) {
        vector<Node> ret(count);
        forn (int, i, count) {
            ret[i] = addNode();
        }
        return ret;
    }

    Edge addEdge(Node u, Node v) {
        assert(valid(u));
        assert(valid(v));
        int n = (int)arcs_.size();
        arcs_.push_back({});
        arcs_[n].to = v.id;
        arcs_[n].nextOut = nodes_[u.id].firstOut;
        nodes_[u.id].firstOut = n;
        arcs_.push_back({});
        arcs_[n + 1].to = u.id;
        arcs_[n + 1].nextOut = nodes_[v.id].firstOut;
        nodes_[v.id].firstOut = n + 1;
        nodes_[u.id].degree++;
        nodes_[v.id].degree++;
        return Edge{n >> 1};
    }

    int nodesCount() const {
        return (int)nodes_.size();
    }

    int arcsCount() const {
        return (int)arcs_.size();
    }

    int edgesCount() const {
        return (int)arcs_.size() / 2;
    }

    Arc dual(Arc a) const {
        if (a.id == -1) {
            return a;
        }
        assert(valid(a));
        return Arc{a.id ^ 1};
    }

    Arc firstInArc(Node a) const {
        assert(valid(a));
        return dual(Arc{nodes_[a.id].firstOut});
    }

    Arc firstOutArc(Node a) const {
        assert(valid(a));
        return Arc{nodes_[a.id].firstOut};
    }

    Arc nextInArc(Arc a) const {
        assert(valid(a));
        return dual(Arc{arcs_[dual(a).id].nextOut});
    }

    Arc nextOutArc(Arc a) const {
        assert(valid(a));
        return Arc{arcs_[a.id].nextOut};
    }

    Node from(Arc a) const {
        assert(valid(a));
        return Node{arcs_[dual(a).id].to};
    }

    Node to(Arc a) const {
        assert(valid(a));
        return Node{arcs_[a.id].to};
    }

    template <class T>
    NodeMap<T> nodeMap(T v0 = T{}) const {
        NodeMap<T> ret(v0);
        ret.reserve(nodesCount());
        return ret;
    }

    template <class T>
    ArcMap<T> arcMap(T v0 = T{}) const {
        ArcMap<T> ret(v0);
        ret.reserve(arcsCount());
        return ret;
    }

private:

    static Arc nextInArc_(const Graph& g, Arc a) {
        return g.nextInArc(a);
    }

    static Arc nextOutArc_(const Graph& g, Arc a) {
        return g.nextOutArc(a);
    }

public:

    graph_impl::GenListIterable<Arc, Graph, &Graph::nextInArc_> inArcs(Node a) const {
        assert(valid(a));
        return graph_impl::GenListIterable<Arc, Graph, &Graph::nextInArc_>(*this, firstInArc(a), Arc{});
    }

    graph_impl::GenListIterable<Arc, Graph, &Graph::nextOutArc_> outArcs(Node a) const {
        assert(valid(a));
        return graph_impl::GenListIterable<Arc, Graph, &Graph::nextOutArc_>(*this, firstOutArc(a), Arc{});
    }

    graph_impl::GenRangeIterable<Node> nodes() const {
        return graph_impl::GenRangeIterable<Node>(0, nodesCount());
    }

    graph_impl::GenRangeIterable<Arc> arcs() const {
        return graph_impl::GenRangeIterable<Arc>(0, arcsCount());
    }

private:
    vector<graph_impl::NodeT> nodes_;
    vector<graph_impl::ArcT> arcs_;
};

// TODO: gap heuristic & relabel sorting heuristic
// https://www.ads.tuwien.ac.at/teaching/archiv/praktika/CherkasskyGoldberg-1995-MaxFlow.pdf
template <class Cap>
struct PushRelabel {
    using Node = Graph::Node;
    using Arc = Graph::Arc;

    PushRelabel(const Graph& g_, const Graph::ArcMap<Cap>& cap_): g(g_), cap(cap_) {}

    Cap run(Node s, Node t) {
        flow = g.arcMap<Cap>();
        residual = cap;
        label = g.nodeMap<int>(g.nodesCount());
        excess = g.nodeMap<Cap>();
        excess(s) = numeric_limits<Cap>::max();
        currentArc = g.nodeMap<Arc>();
        for (auto v : g.nodes()) {
            currentArc(v) = g.firstOutArc(v);
        }
        fullRelabel(t);
        while (currentActiveLabel > 0) {
            if (activeNodesByLabel[currentActiveLabel].empty()) {
                currentActiveLabel--;
                continue;
            }
            auto u = pop_back(activeNodesByLabel[currentActiveLabel]);
            discharge(u);
            currentActiveLabel = label(u);
            if (stepsCount >= g.nodesCount()) {
                fullRelabel(t);
                stepsCount = 0;
            }
        }
        //TODO
        for (auto a : g.arcs()) {
            flow(a) = cap(a) - residual(a);
            if (flow(a) > 0) {
                dbg(g.from(a).id, g.to(a).id, flow(a));
            }
        }
        return value = excess(t);
    }

    void fullRelabel(Node t) {
        label.clear();
        label(t) = 0;
        queue<Node> q;
        q.push(t);
        while (!q.empty()) {
            auto u = pop(q);
            for (auto a : g.inArcs(u)) {
                auto v = g.from(a);
                if (residual(a) > 0 && label(v) == g.nodesCount()) {
                    label(v) = label(u) + 1;
                    q.push(v);
                }
            }
        }
        activeNodesByLabel.clear();
        activeNodesByLabel.resize(g.nodesCount() + 1);
        currentActiveLabel = 0;
        for (auto u : g.nodes()) {
            if (u != t && excess(u) > 0 && label(u) < g.nodesCount()) {
                activeNodesByLabel[label(u)].push_back(u);
                rmx(currentActiveLabel, label(u));
            }
        }
    }

    void relabel(Node u) {
        stepsCount++;
        label(u) = g.nodesCount();
        for (auto a : g.outArcs(u)) {
            if (residual(a) > 0) {
                rmn(label(u), label(g.to(a)) + 1);
            }
        }
    }

    void discharge(Node u) {
        int highestLabel = label(u);
        while (label(u) < g.nodesCount()) {
            auto a = currentArc(u);
            auto v = g.to(a);
            // push(a)
            if (residual(a) > 0 && label(u) == label(v) + 1) {
                int delta = mn(excess(u), residual(a));
                if (excess(v) == 0) {
                    activeNodesByLabel[label(v)].push_back(v);
                }
                excess(u) -= delta;
                excess(v) += delta;
                residual(a) -= delta;
                residual(g.dual(a)) += delta;
                if (excess(u) == 0) {
                    break;
                }
            }
            currentArc(u) = g.nextOutArc(a);
            if (currentArc(u) == Arc{}) {
                relabel(u);
                currentArc(u) = g.firstOutArc(u);
            }
        }
    }

    // outputs
    int value;
    Graph::ArcMap<Cap> flow, residual;

private:
    // inputs
    const Graph& g;
    const Graph::ArcMap<Cap>& cap;

    // temp
    Graph::NodeMap<Cap> excess;
    Graph::NodeMap<int> label;
    Graph::NodeMap<Arc> currentArc;
    int stepsCount = 0;

    vector<vector<Node>> activeNodesByLabel;
    int currentActiveLabel;
};

// ^^^^^^^^^^^^^^^^^ Library code end

void solve(istream& in, ostream& out) {
    int n = next<int>(in);
    Graph g;
    Graph::Node s = g.addNode(), t = g.addNode();
    auto cap = g.arcMap<int>();
    vector<vector<Graph::Node>> levelsIn, levelsOut;
    levelsIn.push_back(g.addNodes(n));
    levelsOut.push_back(g.addNodes(n));
    forn (int, i, n) {
        g.addEdge(s, levelsIn[0][i]).withUV(cap, 1);
        g.addEdge(levelsOut[0][i], t).withUV(cap, 1);
    }
    for (int l = 1; (1 << l) <= n; ++l) {
        levelsIn.push_back(g.addNodes(n - (1 << l) + 1));
        levelsOut.push_back(g.addNodes(n - (1 << l) + 1));
        forn (int, i, n - (1 << l) + 1) {
            g.addEdge(levelsIn[l - 1][i], levelsIn[l][i]).withUV(cap, n);
            g.addEdge(levelsIn[l - 1][i + (1 << (l - 1))], levelsIn[l][i]).withUV(cap, n);
            g.addEdge(levelsOut[l][i], levelsOut[l - 1][i]).withUV(cap, n);
            g.addEdge(levelsOut[l][i], levelsOut[l - 1][i + (1 << (l - 1))]).withUV(cap, n);
        }
    }
    int qs = next<int>(in);
    using evt = tuple<int, int, pair<int, int>>;
    vector<evt> evs;
    forn (int, q, qs) {
        int x1 = next<int>(in) - 1;
        int y1 = next<int>(in) - 1;
        int x2 = next<int>(in) - 1;
        int y2 = next<int>(in) - 1;
        evs.push_back(evt{x1, +1, {y1, y2 + 1}});
        evs.push_back(evt{x2 + 1, -1, {y1, y2 + 1}});
    }
    evs.push_back(evt{0, -1, {0, n}});
    evs.push_back(evt{n, +1, {0, n}});
    sort(all(evs));
    auto insertRect = [&](int x0, int x1, int y0, int y1) {
        if (x0 == x1 || y0 == y1) {
            return;
        }
        int dx = 0, dy = 0;
        while (x0 + (1 << (dx + 1)) <= x1) {
            ++dx;
        }
        while (y0 + (1 << (dy + 1)) <= y1) {
            ++dy;
        }
        auto u = g.addNode();
        g.addEdge(levelsIn[dx][x0], u).withUV(cap, n);
        g.addEdge(levelsIn[dx][x1 - (1 << dx)], u).withUV(cap, n);
        g.addEdge(u, levelsOut[dy][y0]).withUV(cap, n);
        g.addEdge(u, levelsOut[dy][y1 - (1 << dy)]).withUV(cap, n);
        dbg(x0 + 1, y0 + 1, x1, y1, u.id);
    };
    map<int, pair<int, int>> ranges;
    for (auto& e : evs) {
        int x = get<0>(e);
        auto& range = get<2>(e);
        if (get<1>(e) == -1) {
            ranges[range.first] = {range.second, x};
        } else {
            auto it = ranges.upper_bound(range.first);
            --it;
            pair<int, int> outer;
            outer.first = it->first;
            while (it != ranges.end() && it->first < range.second) {
                insertRect(it->second.second, x, it->first, it->second.first);
                outer.second = it->second.first;
                auto jt = it;
                it++;
                ranges.erase(jt);
            }
            if (outer.first < range.first) {
                ranges[outer.first] = {range.first, x};
            }
            if (range.second < outer.second) {
                ranges[range.second] = {outer.second, x};
            }
        }
    }
    PushRelabel<int> f(g, cap);
    out << f.run(s, t) << "\n";
}

int main() {
    solve(cin, cout);
    return 0;
}

//
// Created by Evgeny Savinov on 09/01/2017.
//

#include <bits/stdc++.h>

#define clr(x) memset((x), 0, sizeof(x))
#define all(x) (x).begin(), (x).end()
#define pb push_back
#define mp make_pair
#define x first
#define y second
#define forn(i, n) for(int i = 0; i < (int)(n); ++i)
#define ford(i, n) for(int i = (int)(n) - 1; i >= 0; --i)
#define for1(i, n) for(int i = 1; i <= (int)(n); ++i)

using namespace std;

typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
//typedef pair<long long, long long> pii;
typedef vector<long long> vll;
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef int itn;

const ld PI = 3.1415926535897932384626433832795L;

template<class T>
bool uin(T &, const T &);

template<class T>
bool uax(T &, const T &);

template<class T>
T gcd(T, T);

template<class T>
T lcm(T, T);

template<class _T>
inline string tostr(const _T &);

template<typename T>
void input(T &);

class range {
    using type = long long;
public:
    class iterator {
        friend class range;

    public:
        using difference_type = range::type;
        using value_type = range::type;
        using pointer = const range::type *;
        using reference = const range::type &;
        using iterator_category = std::random_access_iterator_tag;

        value_type operator*() const { return i_; }

        const iterator &operator++() {
            ++i_;
            return *this;
        }

        iterator operator++(int) {
            iterator copy(*this);
            ++i_;
            return copy;
        }

        const iterator &operator--() {
            --i_;
            return *this;
        }

        iterator operator--(int) {
            iterator copy(*this);
            --i_;
            return copy;
        }

        difference_type operator-(const iterator &other) const { return i_ - other.i_; }

        iterator operator+(const difference_type &delta) const { return iterator(i_ + delta); }

        iterator &operator+=(const difference_type &delta) {
            i_ += delta;
            return *this;
        }

        iterator operator-(const difference_type &delta) const { return iterator(i_ - delta); }

        iterator &operator-=(const difference_type &delta) {
            i_ -= delta;
            return *this;
        }

        bool operator==(const iterator &other) const { return i_ == other.i_; }

        bool operator!=(const iterator &other) const { return i_ != other.i_; }

    protected:
        iterator(const value_type &start) : i_(start) {}

    private:
        value_type i_;
    };

    iterator begin() const { return begin_; }

    iterator end() const { return end_; }

    range(const type &begin, const type &end) : begin_(begin), end_(end) {}

    range(const type &end) : begin_(0), end_(end) {}

private:
    iterator begin_;
    iterator end_;
};

template<typename T = long long>
T nxt();

bool checkp(long long);

template<typename T>
T pw(T a, T n, T p);

template<typename T>
T inv(T a, T p);

template<class _T>
_T sqr(const _T &x);

class range;

mt19937_64 gen;

int TTT;

ll mod = 1000000007;

void pre() {

}

struct vertex {
    int l, r;
    int delta;
    int pos;
} data[1 << 20];

const int ss = 22222;

int sz = ss;

int NEW(int l, int r, int delta, int pos) {
    data[sz].l = l;
    data[sz].r = r;
    data[sz].delta = delta;
    data[sz].pos = pos;
    return sz++;
}

int NEW(int pos) {
    data[sz].l = data[sz].r = 0;
    data[sz].delta = 0;
    data[sz].pos = pos;
    return sz++;
}

int build(int tl, int tr) {
    if (tr - tl == 1)
        return NEW(tl);
    int tm = (tl + tr) / 2;
    return NEW(build(tl, tm), build(tm, tr), 0, 0);
}

int update(int t, int tl, int tr, int l, int r, int delta) {
    if (l >= r) return t;

    if (tl == l && tr == r) {
        return NEW(data[t].l, data[t].r, data[t].delta + delta, data[t].pos);
    }
    int tm = (tl + tr) / 2;
    return NEW(update(data[t].l, tl, tm, l, min(r, tm), delta),
               update(data[t].r, tm, tr, max(l, tm), r, delta), data[t].delta, data[t].pos);
}

struct Dinic {
#define SET(p) memset(p, -1, sizeof(p))
#define CLR(p) memset(p, 0, sizeof(p))
#define i64 long long

    static const int INF = 0x7fffffff;
    static const int MAXN = 1 << 20, MAXE = 1 << 22;

    int src, snk, nNode, nEdge;
    int Q[MAXN], fin[MAXN], pro[MAXN], dist[MAXN];
    int flow[MAXE], cap[MAXE], next[MAXE], to[MAXE];

    inline void init(int _src, int _snk, int _n) {
        src = _src, snk = _snk, nNode = _n, nEdge = 0;
        SET(fin);
    }

    inline void add(int u, int v, int c) {
        assert(nEdge < MAXE);
        to[nEdge] = v, cap[nEdge] = c, flow[nEdge] = 0, next[nEdge] = fin[u], fin[u] = nEdge++;
        to[nEdge] = u, cap[nEdge] = 0, flow[nEdge] = 0, next[nEdge] = fin[v], fin[v] = nEdge++;
    }

    bool bfs() {
        int st, en, i, u, v;
        SET(dist);
        dist[src] = st = en = 0;
        Q[en++] = src;
        while (st < en) {
            u = Q[st++];
            for (i = fin[u]; i >= 0; i = next[i]) {
                v = to[i];
                if (flow[i] < cap[i] && dist[v] == -1) {
                    dist[v] = dist[u] + 1;
                    Q[en++] = v;
                }
            }
        }
        return dist[snk] != -1;
    }

    int dfs(int u, int fl) {
        if (u == snk) return fl;
        for (int &e = pro[u], v, df; e >= 0; e = next[e]) {
            v = to[e];
            if (flow[e] < cap[e] && dist[v] == dist[u] + 1) {
                df = dfs(v, min(cap[e] - flow[e], fl));
                if (df > 0) {
                    flow[e] += df;
                    flow[e ^ 1] -= df;
                    return df;
                }
            }
        }
        return 0;
    }

    i64 dinitz() {
        i64 ret = 0;
        int df;
        while (bfs()) {
            memcpy(pro, fin, nNode * sizeof(fin[0]));
            while (true) {
                df = dfs(src, INF);
                if (df) ret += df;
                else break;
            }
        }
        return ret;
    }

#undef CLR
#undef SET
#undef i64
} d;

void solve(int test) {
    int n = nxt();
    int q = nxt();

    int N = n;
//    while (N < n) N <<= 1;

    vector<pii> add[n + 1];
    vector<pii> remove[n + 1];

    forn(i, q) {
        int x1 = nxt() - 1;
        int y1 = nxt() - 1;

        int x2 = nxt();
        int y2 = nxt();

        add[x1].push_back({y1, y2});
        remove[x2].push_back({y1, y2});
    }

    int nodes[n + 1];
    nodes[0] = build(0, N);

    forn(i, n) {
        int x = nodes[i];
        for (auto p : add[i]) {
            x = update(x, 0, N, p.x, p.y, 1);
        }
        for (auto p : remove[i]) {
            x = update(x, 0, N, p.x, p.y, -1);
        }
        nodes[i + 1] = x;
    }

    int s = n + n;
    int t = n + n + 1;

    d.init(s, t, sz);

    forn(i, n) {
        d.add(s, i, 1);
        d.add(n + i, t, 1);
        d.add(i, nodes[i + 1], n);
    }

    for (int i = ss; i < sz; ++i) {
        if (data[i].l) {
            if (data[i].delta == 0) {
                d.add(i, data[i].l, n);
                d.add(i, data[i].r, n);
            }
        } else {
            if (data[i].delta == 0 && data[i].pos < n) {
                d.add(i, n + data[i].pos, n);
            }
        }
    }
    cerr << d.nEdge << endl;
    int ans = d.dinitz();
    cout << ans << "\n";
}

int main(int argc, char **argv) {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
#define fname "sequence"
    //freopen(fname".in", "r", stdin);
    //freopen(fname".out", "w", stdout);
#endif
    pre();
    ::TTT = 1;
#ifdef LOCAL
#else
#endif

    for (int test = 1; test <= ::TTT; ++test) {
        solve(test);
    }

#ifdef LOCAL
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

template<typename T>
T gcd(T x, T y) {
    while (y > 0) {
        x %= y;
        swap(x, y);
    }
    return x;
}

template<class T>
T lcm(T a, T b) {
    return a / gcd(a, b) * b;
}

template<class _T>
inline _T sqr(const _T &x) {
    return x * x;
}

template<class _T>
inline string tostr(const _T &a) {
    ostringstream os("");
    os << a;
    return os.str();
}

template<typename T>
inline void input(T &a) {
    static int ed;
    a = 0;
    while (!isdigit(ed = getchar()) && ed != '-') {}
    char neg = 0;
    if (ed == '-') {
        neg = 1;
        ed = getchar();
    }
    while (isdigit(ed)) {
        a = 10 * a + ed - '0';
        ed = getchar();
    }
    if (neg) a = -a;
}

template<typename T = long long>
inline T nxt() {
    T res;
    input(res);
    return res;
}

void myassert(bool v) {
    assert(v);
//cout << "FAIL\n";
//exit(0);
}

mt19937 generator;

bool checkp(long long v) {
    if (v < 2) return false;
    for (long long i = 2; i * i <= v; ++i) {
        if (v % i == 0) {
            return false;
        }
    }
    return true;
}

template<typename T>
T pw(T a, T n, T m) {
    T res = 1;
    while (n) {
        if (n & 1) {
            res = res * a % m;
        }
        a = a * a % m;
        n >>= 1;
    }
    return res;
}

template<typename T>
T inv(T a, T p) {
    T res = 1;
    while (a > 1) {
        res = res * (p - p / a) % p;
        a = p % a;
    }
    return res;
}

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

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

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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXN 50005

int n,q,S,T,top = 1,to[MAXM],wei[MAXM],nxt[MAXM],head[MAXM];
void Add_Edge (int u,int v,int w){
	to[++ top] = v,wei[top] = w,nxt[top] = head[u],head[u] = top;
	to[++ top] = u,wei[top] = 0,nxt[top] = head[v],head[v] = top;
}
int dep[MAXM];
bool BFS (int s,int t){
	for (Int i = 1;i <= T;++ i) dep[i] = INF;
	queue <int> q;while (!q.empty()) q.pop ();dep[s] = 0,q.push (s);
	while (!q.empty()){
		int u = q.front();q.pop ();
		for (Int i = head[u];i;i = nxt[i]){
			int v = to[i],w = wei[i];
			if (dep[v] == INF && w){
				dep[v] = dep[u] + 1;
				q.push (v);
			}
		}
	}
	return dep[t] != INF;
}
int dfs (int s,int t,int lim){
	if (s == t) return lim;
	int used = 0;
	for (Int i = head[s];i && lim;i = nxt[i]){
		int v = to[i],w = wei[i];
		if (dep[v] == dep[s] + 1 && w){
			int del = dfs (v,t,min (lim,w));
			used += del,lim -= del,wei[i] -= del,wei[i ^ 1] += del;
		}
	}
	if (used == 0) dep[s] = INF;
	return used;
}
int Dinic (int s,int t){
	int res = 0;
	while (BFS (s,t)) res += dfs (s,t,INF);
	return res;
}
int sizt;
int id (int l,int r,int type){return type * sizt + ((l + r) | (l != r));}
void build (int l,int r,int type){
	int rt = id (l,r,type);
	if (l == r){
		if (type == 0) Add_Edge (S,rt,1);
		if (type == 1) Add_Edge (rt,T,1);
		return ;
	}
	int mid = (l + r) >> 1;
	build (l,mid,type),build (mid + 1,r,type);
	if (type == 0) Add_Edge (id (l,mid,type),rt,INF),Add_Edge (id (mid + 1,r,type),rt,INF);
	else Add_Edge (rt,id (l,mid,type),INF),Add_Edge (rt,id (mid + 1,r,type),INF);
}

#define PII pair<int,int>
#define MP make_pair

void modify (int l,int r,int type,int tl,int tr,vector<PII> &s){
	if (l >= tl && r <= tr) return s.push_back (MP (id (l,r,type),r - l + 1));
	int mid = (l + r) >> 1;
	if (tl <= mid) modify (l,mid,type,tl,tr,s);
	if (tr > mid) modify (mid + 1,r,type,tl,tr,s);
}
void update (int tl,int tr,int ql,int qr){
	static vector <PII> A,B;A.clear(),B.clear();
	modify (1,n,0,tl,tr,A),modify (1,n,1,ql,qr,B);
	for (Int i = 0;i < A.size();++ i) for (Int j = 0;j < B.size();++ j) Add_Edge (A[i].first,B[j].first,A[i].second * B[j].second);
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int lef[MAXN];
void Scan (){//输入+扫描线
	read (n,q);int tot = 0;
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	sort (mat + 1,mat + tot + 1);
	sizt = id (n,n,0);
	S = sizt * 2 + 3,T = S + 1;
	build (1,n,0),build (1,n,1);
	for (Int i = 1;i <= tot;++ i){
		if (mat[i].type == 1) for (Int j = mat[i].r1;j <= mat[i].r2;++ j) lef[j] = mat[i].c;
		else{
			for (Int j = mat[i].r1,lasw = mat[i].c - 1,lash = 0;j <= mat[i].r2 + 1;++ j){
				if (j == mat[i].r2 + 1 || lef[j] ^ lasw){
					if (lasw + 1 < mat[i].c) update (lash,j - 1,lasw + 1,mat[i].c - 1);
					lasw = lef[j],lash = j;
				}
			}
		}
	}
}

signed main(){
	Scan ();
	write (Dinic (S,T)),putchar ('\n');
	return 0;
}

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

template <class T> struct Edge {
    int from, to;
    T cap, flow;
    int index;

    Edge(int from, int to, T cap, T flow, int index): from(from), to(to), cap(cap), flow(flow), index(index) {}
};

template <class T> struct PushRelabel {
    int n;
    vector <vector <Edge <T>>> adj;
    vector <T> excess;
    vector <int> heights, count;
    vector <bool> active;
    vector <vector <int>> buckets;
    int max_height;
    queue <int> Q;

    const int source = 0, sink = 1;

    PushRelabel() : n(2), adj(2) {}

    int add_vertex() {
        adj.push_back({});
        return n++;
    }

    void add_edge(int from, int to, int cap) {
        adj[from].push_back(Edge <T>(from, to, cap, 0, int(adj[to].size())));
        if (from == to) {
            adj[from].back().index++;
        }
        adj[to].push_back(Edge <T>(to, from, 0, 0, int(adj[from].size()) - 1));

    }

    void Enqueue (int v)
    {
        if (!active[v] && excess[v] > 0 && heights[v] < n)
        {
            active[v] = true;
            buckets[heights[v]].push_back(v);
            max_height = max(max_height, heights[v]);
        }
    }

    void Gap (int k) {
        for (int v = 0; v < n; v++) if (heights[v] >= k) {
            count[heights[v]]--;
            heights[v] = max(heights[v], n);
            assert(heights[v] == n);
            count[heights[v]]++;
            Enqueue(v);
        }
    }

    void Relabel (int v)
    {
        count[heights[v]]--;
        heights[v] = n;
        for (auto e: adj[v]) if (e.cap - e.flow > 0) {
            heights[v] = min(heights[v], heights[e.to] + 1);
        }
        count[heights[v]]++;
        Enqueue(v);
    }

    T maxflow ()
    {
        heights = vector<int>(n, 0);
        excess = vector<T>(n, 0);
        count = vector <int>(n + 1, 0);
        active = vector <bool>(n, false);
        buckets = vector <vector <int>>(n);
        max_height = 0;

        for (auto &e: adj[source])
            excess[source] += e.cap;

        count[0] = n;
        Enqueue(source);
        active[sink] = true;

        while (max_height >= 0)
        {
            if (!buckets[max_height].empty())
            {
                int v = buckets[max_height].back();
                buckets[max_height].pop_back();
                active[v] = false;

                for (auto &e: adj[v])
                {
                    if (excess[v] == 0) break;
                    const auto delta = min(excess[e.from], e.cap - e.flow);
                    if (heights[e.to] == heights[e.from]-1 && delta)
                    {
                        e.flow += delta;
                        adj[e.to][e.index].flow -= delta;
                        excess[e.to] += delta;
                        excess[e.from] -= delta;
                        Enqueue(e.to);
                    }
                }

                if (excess[v] > 0)
                {
                    if (count[heights[v]] == 1) Gap(heights[v]);
                    else Relabel(v);
                }
            }
            else max_height--;
        }
        return excess[sink];
    }
};

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

    auto network = PushRelabel<int>();

    int n; cin >> n;

    auto vertices = array<vector<int>, 2>{};
    for (auto& vs : vertices) vs = vector<int>(4*n);

    const function<int(int, int, int, bool)> build = [&](const int i, const int l, const int r, const bool direction)
    {
        const auto v = vertices[direction][i] = network.add_vertex();
        if (r-l == 1)
        {
            if (direction) network.add_edge(network.source, v, 1);
            else network.add_edge(v, network.sink, 1);
        }
        else
        {
            const auto m = (l+r)/2;
            const auto w1 = build(2*i, l, m, direction);
            const auto w2 = build(2*i+1, m, r, direction);
            if (direction)
            {
                network.add_edge(w1, v, n);
                network.add_edge(w2, v, n);
            }
            else
            {
                network.add_edge(v, w1, n);
                network.add_edge(v, w2, n);
            }
        }
        return v;
    };
    for (auto direction : {true, false}) build(1, 0, n, direction);

    using Segment = array<int, 2>;
    using Rectangle = array<Segment, 2>;
    const auto add_rectangle = [&](const Rectangle q)
    {
        if (q[0][0] == q[0][1] || q[1][0] == q[1][1]) return;

        const auto v = network.add_vertex();
        for (const auto direction : {true, false})
        {
            const auto ql = q[direction][0], qr = q[direction][1];
            const function<void(int, int, int)> connect = [&](const int i, const int l, const int r)
            {
                if (ql >= r || l >= qr) return;
                else if (ql <= l && r <= qr)
                {
                    if (direction) network.add_edge(vertices[direction][i], v, n);
                    else network.add_edge(v, vertices[direction][i], n);
                }
                else
                {
                    const auto m = (l+r)/2;
                    connect(2*i, l, m);
                    connect(2*i+1, m, r);
                }
            };
            connect(1, 0, n);
        }
    };

    int q; cin >> q;
    struct Event { bool type; int x; Segment y; };
    auto events = vector<Event>();
    for (auto i = 0; i < q; i++)
    {
        int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        x1--; y1--; x2--; y2--;

        events.push_back({true, x1, {y1, y2+1}});
        events.push_back({false, x2+1, {y1, y2+1}});
    }
    events.push_back({false, 0, {0, n}});
    events.push_back({true, n, {0, n}});
    const auto event_compare = [](const Event a, const Event b)
    {
        if (a.x != b.x) return a.x < b.x;
        else return a.type < b.type;
    };
    sort(begin(events), end(events), event_compare);

    struct PartialRectangle { int x1; Segment y; };
    const auto rect_compare = [](const PartialRectangle a, const PartialRectangle b) { return a.y[0] < b.y[0]; };
    auto active = set<PartialRectangle, decltype(rect_compare)>(rect_compare);
    for (const auto e : events)
    {
        if (e.type)
        {
            auto to_insert = vector<PartialRectangle>();

            auto i = active.lower_bound({0, e.y[0]});
            if (i != begin(active) && prev(i)->y[1] > e.y[0])
                i = prev(i);

            while (i != end(active) && i->y[0] < e.y[1])
            {
                if (i->y[0] < e.y[0]) to_insert.push_back({e.x, {i->y[0], e.y[0]}});
                if (e.y[1] < i->y[1]) to_insert.push_back({e.x, {e.y[1], i->y[1]}});
                add_rectangle({{{i->x1, e.x}, i->y}});
                active.erase(i++);
            }

            for (const auto r : to_insert) active.insert(r);
        }
        else active.insert({e.x, e.y});
    }

    cout << network.maxflow() << endl;
}

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 10010
#define M 80000
#define INF 1e9
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
struct Node{
    int x,l,r;
    int f;
    Node(int x=0,int l=0,int r=0,int f=0):x(x),l(l),r(r),f(f){}
}c[N<<1];
struct Edge{
    int t,nx,c;
}e[3000000];
int h[M],d[M],q[M],cur[M];
bool b[M];
int i,j,k,n,m,p,a[N];
int x1,x2,y1,y2,s,t,cnt;
int num=1,r1,r2,ls[N<<3],rs[N<<3];
inline void Add(int x,int y,int z){
    e[++num].t=y;e[num].c=z;e[num].nx=h[x];h[x]=num;
    e[++num].t=x;e[num].c=0;e[num].nx=h[y];h[y]=num;
}
inline void Build(int& x,int y,int l,int r,int d){
    x=++cnt;
    if(y){
        if(!d)Add(x,y,INF);else Add(y,x,INF);
    }
    if(l==r)return;
    int Mid=l+r>>1;
    Build(ls[x],x,l,Mid,d);Build(rs[x],x,Mid+1,r,d);
}
inline void Build2(int x,int l,int r,int y){
    if(l==r){
        if(!y)Add(0,x,1);else Add(x,t,1);
        return;
    }
    int Mid=l+r>>1;
    Build2(ls[x],l,Mid,y);Build2(rs[x],Mid+1,r,y);
}
inline bool Cmp(Node a,Node b){
    if(a.x==b.x)return a.f>b.f;
    return a.x<b.x;
}
inline void Query(int x,int l,int r,int L,int R,int y,bool d){
    if(!x||l>R||r<L)return;
    if(l>=L&&r<=R){
        if(!d)Add(x,y,INF);else Add(y,x,INF);
        return;
    }
    int Mid=l+r>>1;
    Query(ls[x],l,Mid,L,R,y,d);
    Query(rs[x],Mid+1,r,L,R,y,d);
}
inline void Insert(int x,int l,int r){
    int pos=-1,last=-1,y;
    for(int i=l;i<=r+1;i++){
        if(i<=r)y=a[i]+1;
        if(y!=last||i==r+1){
            if(pos!=-1){
                int z=++cnt;
                Query(r1,1,n,pos,i-1,z,0);
                Query(r2,1,n,last,x,z,1);
            }
            pos=i;last=y;
        }
    }
}
inline void Modify(int x,int l,int r){
    for(int i=l;i<=r;i++)a[i]=x;
}
inline bool Bfs(){
    memset(b,0,sizeof(b));
    b[s]=1;int l=0,r=1;q[1]=s;
    while(++l<=r){
        int x=q[l];
        for(int i=h[x];i;i=e[i].nx)
        if(e[i].c&&!b[e[i].t]){
            b[e[i].t]=1;d[e[i].t]=d[x]+1;q[++r]=e[i].t;
        }
    }
    return b[t];
}
inline int Dfs(int x,int y){
    if(!y||x==t)return y;
    int k=y,f;
    for(int& i=cur[x];i;i=e[i].nx){
        int v=e[i].t;
        if(d[v]==d[x]+1&&(f=Dfs(v,min(y,e[i].c)))){
            y-=f;e[i].c-=f;e[i^1].c+=f;
            if(!y)break;
        }
    }
    if(k==y)d[x]=-1;
    return k-y;
}
inline int Dinic(){
    int Ans=0;
    while(Bfs()){
        for(int i=0;i<=cnt;i++)cur[i]=h[i];
        Ans+=Dfs(s,INF);
    }
    return Ans;
}
int main(){
    Read(n);Read(m);
    Build(r1,0,1,n,0);Build(r2,0,1,n,1);
    for(i=1;i<=m;i++){
        Read(x1);Read(y1);Read(x2);Read(y2);
        c[i*2-1]=Node(x1-1,y1,y2,0);c[i*2]=Node(x2,y1,y2,1);
    }
    sort(c+1,c+m*2+1,Cmp);
    for(i=1;i<=m*2;i++)
    if(!c[i].f)Insert(c[i].x,c[i].l,c[i].r);else Modify(c[i].x,c[i].l,c[i].r);
    Insert(n,1,n);
    t=++cnt;
    Build2(r1,1,n,s);Build2(r2,1,n,t);
    printf("%d\n",Dinic());
    return 0;
}

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define MAXN 10000
#define MAXQ 10000
#define INF 0x3FFFFFFF
using namespace std;
struct Line
{
	int p,l,r,typ;
	Line(){};
	Line(int _p,int _l,int _r,int _typ):p(_p),l(_l),r(_r),typ(_typ){};
};
bool operator < (const Line &a,const Line &b)
{
	if(a.p!=b.p)	return a.p<b.p;
	return a.typ>b.typ;
}
struct edge
{
	int to,cap;
	edge *nxt,*bck;
}edges[MAXN*500];
edge *ncnt=&edges[0],*Adj[MAXN*10+5];
struct node
{
	int ch[2];
}tree[MAXN*10];
int tcnt=0,rt[2];
vector<Line> Lineseq;
vector<int> seq[2];
int n,q,S,T;
int a[MAXN+5];
int d[MAXN*10],vd[MAXN*10];
void AddEdge(int u,int v,int cap)
{
	edge *p=++ncnt;
	p->to=v,p->cap=cap;
	p->nxt=Adj[u],Adj[u]=p;

	edge *q=++ncnt;
	q->to=u,q->cap=0;
	q->nxt=Adj[v],Adj[v]=q;

	p->bck=q,q->bck=p;
}
void Build(int &p,int l,int r)
{
	p=++tcnt;
	if(l==r)
		return;
	int mid=(l+r)/2;
	Build(tree[p].ch[0],l,mid);
	Build(tree[p].ch[1],mid+1,r);
}
void Bianli(int p,int l,int r,int typ)
{
	if(l==r)
	{
		if(typ==0)	AddEdge(S,p,1);//参考之前网络流的图
		else		AddEdge(p,T,1);
		return;
	}
	int mid=(l+r)/2;
	//最后网络流的图的形状十分特殊
	if(typ==0)
		AddEdge(tree[p].ch[0],p,INF),AddEdge(tree[p].ch[1],p,INF);
	if(typ==1)
		AddEdge(p,tree[p].ch[0],INF),AddEdge(p,tree[p].ch[1],INF);
	Bianli(tree[p].ch[0],l,mid,typ);
	Bianli(tree[p].ch[1],mid+1,r,typ);
}
void Query(int p,int ql,int qr,int l,int r,int typ)
{
	if(qr<l||ql>r)
		return;
	if(ql<=l&&r<=qr)
	{
		seq[typ].push_back(p);
		return;
	}
	int mid=(l+r)/2;
	Query(tree[p].ch[0],ql,qr,l,mid,typ);
	Query(tree[p].ch[1],ql,qr,mid+1,r,typ);
}
void Insert_Rectangle(int x1,int y1,int x2,int y2)
{
	seq[0].clear(),seq[1].clear();
	Query(rt[0],x1,x2,1,n,0);Query(rt[1],y1,y2,1,n,1);
	for(int i=0;i<(int)seq[0].size();i++)//先把对应的子线段找出来，然后两两连边
		for(int j=0;j<(int)seq[1].size();j++)
			AddEdge(seq[0][i],seq[1][j],INF);
}
void Scan()
{
	Line cur;
	fill(a,a+1+n,1);
	for(int i=0;i<(int)Lineseq.size();i++)
	{
		cur=Lineseq[i];
		int las=a[cur.l],lasp=cur.l;//last，lastpos的初值（可以画个图理解一下）
		for(int p=cur.l;p<=cur.r;p++)
			if(cur.typ==0)
			{
				if(las!=a[p])
				{
					if(cur.p!=las)//插入前记得判断这个矩阵是否存在（而不是一根线）
						Insert_Rectangle(lasp,las,p-1,cur.p-1);//插入白色矩阵
					las=a[p],lasp=p;
				}
//				a[p]=cur.p+1;
			}
			else
				a[p]=cur.p;
		if(cur.typ==0&&cur.p!=las)//同样需要进行判断
			Insert_Rectangle(lasp,las,cur.r,cur.p-1);
	}
}
int aug(int u,int tot)
{
	if(u==T)
		return tot;
	int v,delta,sum=0,mind=T+1;
	if(d[u]<mind)
		mind=d[u];
	for(edge *p=Adj[u];p!=NULL;p=p->nxt)
	{
		v=p->to;
		if(p->cap>0)
		{
			if(d[u]==d[v]+1)
			{
				delta=min(tot-sum,p->cap);
				delta=aug(v,delta);
				sum+=delta;
				p->cap-=delta,p->bck->cap+=delta;
				if(d[S]>=T+1)
					return sum;
				if(sum==tot)
					break;
			}
		}
	}
	if(sum==0)
	{
		vd[d[u]]--;
		if(vd[d[u]]==0)
			d[S]=T+1;
		d[u]=mind+1;
		vd[d[u]]++;
	}
	return sum;
}
int Isap()
{
	int flow=0;
	memset(d,0,sizeof(d));
	memset(vd,0,sizeof(vd));
	vd[0]=T+1;
	while(d[S]<T+1)
		flow+=aug(S,INF);
	return flow;
}
int main()
{
	scanf("%d %d",&n,&q);
	int x1,y1,x2,y2;
	for(int i=1;i<=q;i++)
	{
		scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
		Lineseq.push_back(Line(y1,x1,x2,0));
		Lineseq.push_back(Line(y2+1,x1,x2,1));
	}
	Lineseq.push_back(Line(n+1,1,n,0));//插入特殊矩阵
	Lineseq.push_back(Line(n+2,1,n,1));
	sort(Lineseq.begin(),Lineseq.end());
	Build(rt[0],1,n);Build(rt[1],1,n);//线段树初始化
	S=0,T=tcnt+1;
	Bianli(rt[0],1,n,0);Bianli(rt[1],1,n,1);//"遍历"来进行线段树之间的连边
	Scan();//扫描线
	//预处理完毕
	int ans=Isap();
	printf("%d\n",ans);
	return 0;
}

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

template <class T> struct Edge {
    int from, to;
    T cap, flow;
    int index;

    Edge(int from, int to, T cap, T flow, int index): from(from), to(to), cap(cap), flow(flow), index(index) {}
};

template <class T> struct PushRelabel {
    int n;
    vector <vector <Edge <T>>> adj;
    vector <T> excess;
    vector <int> dist, count;
    vector <bool> active;
    vector <vector <int>> B;
    int b;
    queue <int> Q;

    const int source = 0, sink = 1;

    PushRelabel() : n(2), adj(2) {}

    int add_vertex() {
        adj.push_back({});
        return n++;
    }

    void add_edge(int from, int to, int cap) {
        adj[from].push_back(Edge <T>(from, to, cap, 0, int(adj[to].size())));
        if (from == to) {
            adj[from].back().index++;
        }
        adj[to].push_back(Edge <T>(to, from, 0, 0, int(adj[from].size()) - 1));

    }

    void Enqueue (int v) {
        if (!active[v] && excess[v] > 0 && dist[v] < n) {
            active[v] = true;
            B[dist[v]].push_back(v);
            b = max(b, dist[v]);
        }
    }

    void Push (Edge <T> &e) {
        T amt = min(excess[e.from], e.cap - e.flow);
        if (dist[e.from] == dist[e.to] + 1 && amt > T(0)) {
            e.flow += amt;
            adj[e.to][e.index].flow -= amt;
            excess[e.to] += amt;
            excess[e.from] -= amt;
            Enqueue(e.to);
        }
    }

    void Gap (int k) {
        for (int v = 0; v < n; v++) if (dist[v] >= k) {
            count[dist[v]]--;
            dist[v] = max(dist[v], n);
            count[dist[v]]++;
            Enqueue(v);
        }
    }

    void Relabel (int v) {
        count[dist[v]]--;
        dist[v] = n;
        for (auto e: adj[v]) if (e.cap - e.flow > 0) {
            dist[v] = min(dist[v], dist[e.to] + 1);
        }
        count[dist[v]]++;
        Enqueue(v);
    }

    void Discharge(int v) {
        for (auto &e: adj[v]) {
            if (excess[v] > 0) {
                Push(e);
            } else {
                break;
            }
        }

        if (excess[v] > 0) {
            if (count[dist[v]] == 1) {
                Gap(dist[v]);
            } else {
                Relabel(v);
            }
        }
    }

    T maxflow () {
        dist = vector <int>(n, 0), excess = vector<T>(n, 0), count = vector <int>(n + 1, 0), active = vector <bool>(n, false), B = vector <vector <int>>(n), b = 0;

        for (auto &e: adj[source]) {
            excess[source] += e.cap;
        }

        count[0] = n;
        Enqueue(source);
        active[sink] = true;

        while (b >= 0) {
            if (!B[b].empty()) {
                int v = B[b].back();
                B[b].pop_back();
                active[v] = false;
                Discharge(v);
            } else {
                b--;
            }
        }
        return excess[sink];
    }
};

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

    auto network = PushRelabel<int>();

    int n; cin >> n;

    auto vertices = array<vector<int>, 2>{};
    for (auto& vs : vertices) vs = vector<int>(4*n);

    const function<int(int, int, int, bool)> build = [&](const int i, const int l, const int r, const bool direction)
    {
        const auto v = vertices[direction][i] = network.add_vertex();
        if (r-l == 1)
        {
            if (direction) network.add_edge(network.source, v, 1);
            else network.add_edge(v, network.sink, 1);
        }
        else
        {
            const auto m = (l+r)/2;
            const auto w1 = build(2*i, l, m, direction);
            const auto w2 = build(2*i+1, m, r, direction);
            if (direction)
            {
                network.add_edge(w1, v, n);
                network.add_edge(w2, v, n);
            }
            else
            {
                network.add_edge(v, w1, n);
                network.add_edge(v, w2, n);
            }
        }
        return v;
    };
    for (auto direction : {true, false}) build(1, 0, n, direction);

    using Segment = array<int, 2>;
    using Rectangle = array<Segment, 2>;
    const auto add_rectangle = [&](const Rectangle q)
    {
        if (q[0][0] == q[0][1] || q[1][0] == q[1][1]) return;

        const auto v = network.add_vertex();
        for (const auto direction : {true, false})
        {
            const auto ql = q[direction][0], qr = q[direction][1];
            const function<void(int, int, int)> connect = [&](const int i, const int l, const int r)
            {
                if (ql >= r || l >= qr) return;
                else if (ql <= l && r <= qr)
                {
                    if (direction) network.add_edge(vertices[direction][i], v, n);
                    else network.add_edge(v, vertices[direction][i], n);
                }
                else
                {
                    const auto m = (l+r)/2;
                    connect(2*i, l, m);
                    connect(2*i+1, m, r);
                }
            };
            connect(1, 0, n);
        }
    };

    int q; cin >> q;
    struct Event { bool type; int x; Segment y; };
    auto events = vector<Event>();
    for (auto i = 0; i < q; i++)
    {
        int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        x1--; y1--; x2--; y2--;

        events.push_back({true, x1, {y1, y2+1}});
        events.push_back({false, x2+1, {y1, y2+1}});
    }
    events.push_back({false, 0, {0, n}});
    events.push_back({true, n, {0, n}});
    const auto event_compare = [](const Event a, const Event b)
    {
        if (a.x != b.x) return a.x < b.x;
        else return a.type < b.type;
    };
    sort(begin(events), end(events), event_compare);

    struct PartialRectangle { int x1; Segment y; };
    const auto rect_compare = [](const PartialRectangle a, const PartialRectangle b) { return a.y[0] < b.y[0]; };
    auto active = set<PartialRectangle, decltype(rect_compare)>(rect_compare);
    for (const auto e : events)
    {
        if (e.type)
        {
            auto to_insert = vector<PartialRectangle>();

            auto i = active.lower_bound({0, e.y[0]});
            if (i != begin(active) && prev(i)->y[1] > e.y[0])
                i = prev(i);

            while (i != end(active) && i->y[0] < e.y[1])
            {
                if (i->y[0] < e.y[0]) to_insert.push_back({e.x, {i->y[0], e.y[0]}});
                if (e.y[1] < i->y[1]) to_insert.push_back({e.x, {e.y[1], i->y[1]}});
                add_rectangle({{{i->x1, e.x}, i->y}});
                active.erase(i++);
            }

            for (const auto r : to_insert) active.insert(r);
        }
        else active.insert({e.x, e.y});
    }

    cout << network.maxflow() << endl;
}

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

struct Network
{
    int V = 2;
    const int source = 0, sink = 1;
    struct Edge { int source, target, id, reverse; };
    vector<vector<Edge>> adjacency_list;
    vector<int> capacities;

    Network() : adjacency_list(2) {}

    int add_vertex()
    {
        adjacency_list.push_back({});
        return V++;
    }

    void add_edge(const int source, const int target, const int capacity)
    {
        if (source == target) return;
        const auto id = int(capacities.size());
        const auto reverse = id+1;
        adjacency_list[source].push_back({source, target, id, reverse});
        adjacency_list[target].push_back({target, source, reverse, id});
        capacities.insert(end(capacities), {capacity, 0});
    }

    int maxflow()
    {
        auto heights = vector<int>(V);
        heights[source] = V;

        auto counts = vector<int>(2*V);
        counts[0] = V-1;
        counts[V] = 1;

        auto active = vector<bool>(V);
        active[source] = active[sink] = true;

        auto excesses = vector<int>(V);
        auto queue = std::queue<int>();
        const auto enqueue = [&](const int v)
        {
            if (active[v]) return;
            else active[v] = true;
            queue.push(v);
        };

        const auto push = [&](const Edge e)
        {
            const auto delta = min(excesses[e.source], capacities[e.id]);
            capacities[e.id] -= delta;
            capacities[e.reverse] += delta;
            excesses[e.source] -= delta;
            excesses[e.target] += delta;
            enqueue(e.target);
        };

        for (const auto e : adjacency_list[source])
        {
            excesses[source] = capacities[e.id];
            push(e);
        }

        while (!queue.empty())
        {
            const auto v = queue.front(); queue.pop();
            active[v] = false;

            for (const auto e : adjacency_list[v])
            {
                if (excesses[v] == 0) break;
                if (heights[e.target] == heights[e.source]-1 && capacities[e.id]) push(e);
            }

            if (excesses[v] > 0)
            {
                if (counts[heights[v]] == 1)
                {
                    const auto cutoff = heights[v];
                    for (auto w = 0; w < V; w++) if (heights[w] >= cutoff)
                    {
                        counts[heights[w]]--;
                        heights[w] = max(heights[w], V+1);
                        counts[heights[w]]++;
                        if (excesses[w]) enqueue(w);
                    }
                }
                else
                {
                    counts[heights[v]]--;
                    heights[v] = 2*V;
                    for (const auto e : adjacency_list[v]) if (capacities[e.id])
                        heights[v] = min(heights[v], heights[e.target] + 1);
                    counts[heights[v]]++;
                    enqueue(v);
                }
            }
        }

        return excesses[sink];
    };
};

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

    auto network = Network();

    int n; cin >> n;

    auto vertices = array<vector<int>, 2>{};
    for (auto& vs : vertices) vs = vector<int>(4*n);

    const function<int(int, int, int, bool)> build = [&](const int i, const int l, const int r, const bool direction)
    {
        const auto v = vertices[direction][i] = network.add_vertex();
        if (r-l == 1)
        {
            if (direction) network.add_edge(network.source, v, 1);
            else network.add_edge(v, network.sink, 1);
        }
        else
        {
            const auto m = (l+r)/2;
            const auto w1 = build(2*i, l, m, direction);
            const auto w2 = build(2*i+1, m, r, direction);
            if (direction)
            {
                network.add_edge(w1, v, n);
                network.add_edge(w2, v, n);
            }
            else
            {
                network.add_edge(v, w1, n);
                network.add_edge(v, w2, n);
            }
        }
        return v;
    };
    for (auto direction : {true, false}) build(1, 0, n, direction);

    using Segment = array<int, 2>;
    using Rectangle = array<Segment, 2>;
    const auto add_rectangle = [&](const Rectangle q)
    {
        if (q[0][0] == q[0][1] || q[1][0] == q[1][1]) return;

        const auto v = network.add_vertex();
        for (const auto direction : {true, false})
        {
            const auto ql = q[direction][0], qr = q[direction][1];
            const function<void(int, int, int)> connect = [&](const int i, const int l, const int r)
            {
                if (ql >= r || l >= qr) return;
                else if (ql <= l && r <= qr)
                {
                    if (direction) network.add_edge(vertices[direction][i], v, n);
                    else network.add_edge(v, vertices[direction][i], n);
                }
                else
                {
                    const auto m = (l+r)/2;
                    connect(2*i, l, m);
                    connect(2*i+1, m, r);
                }
            };
            connect(1, 0, n);
        }
    };

    int q; cin >> q;
    struct Event { bool type; int x; Segment y; };
    auto events = vector<Event>();
    for (auto i = 0; i < q; i++)
    {
        int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        x1--; y1--; x2--; y2--;

        events.push_back({true, x1, {y1, y2+1}});
        events.push_back({false, x2+1, {y1, y2+1}});
    }
    events.push_back({false, 0, {0, n}});
    events.push_back({true, n, {0, n}});
    const auto event_compare = [](const Event a, const Event b)
    {
        if (a.x != b.x) return a.x < b.x;
        else return a.type < b.type;
    };
    sort(begin(events), end(events), event_compare);

    struct PartialRectangle { int x1; Segment y; };
    const auto rect_compare = [](const PartialRectangle a, const PartialRectangle b) { return a.y[0] < b.y[0]; };
    auto active = set<PartialRectangle, decltype(rect_compare)>(rect_compare);
    for (const auto e : events)
    {
        if (e.type)
        {
            auto to_insert = vector<PartialRectangle>();

            auto i = active.lower_bound({0, e.y[0]});
            if (i != begin(active) && prev(i)->y[1] > e.y[0])
                i = prev(i);

            while (i != end(active) && i->y[0] < e.y[1])
            {
                if (i->y[0] < e.y[0]) to_insert.push_back({e.x, {i->y[0], e.y[0]}});
                if (e.y[1] < i->y[1]) to_insert.push_back({e.x, {e.y[1], i->y[1]}});
                add_rectangle({{{i->x1, e.x}, i->y}});
                active.erase(i++);
            }

            for (const auto r : to_insert) active.insert(r);
        }
        else active.insert({e.x, e.y});
    }

    cout << network.maxflow() << endl;
}

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

struct Network
{
    using ll = long long;

    int N = 2, source = 0, sink = 1;
    struct Edge { int source, target, id, reverse; };
    vector<vector<Edge>> adjacency_lists;
    vector<ll> capacities, flows;

    vector<ll> excesses;
    vector<int> heights, counts;
    vector<vector<int>> buckets;
    int highest_active = -1;
    vector<vector<Edge>::iterator> cursors;
    int countdown_to_global_relabel = 0;

    Network() : adjacency_lists(2) {}

    int add_vertex()
    {
        adjacency_lists.push_back({});
        return N++;
    }

    void add_edge(const int source, const int target, const ll capacity)
    {
        if (source == target) return;
        const auto id = int(capacities.size());
        const auto reverse = id+1;
        adjacency_lists[source].push_back({source, target, id, reverse});
        adjacency_lists[target].push_back({target, source, reverse, id});
        capacities.insert(end(capacities), {capacity, 0});
        flows.insert(end(flows), {0, 0});
    }

    void global_relabel()
    {
        countdown_to_global_relabel = N;
        fill(begin(heights), end(heights), N);
        heights[sink] = 0;
        auto queue = std::queue<int>({sink});
        while (!queue.empty())
        {
            const auto v = queue.front();
            queue.pop();
            for (const auto e : adjacency_lists[v])
                if (flows[e.reverse] < capacities[e.reverse] &&
                        heights[e.target] == N)
                {
                    heights[e.target] = heights[v] + 1;
                    queue.push(e.target);
                }
        }

        fill(begin(counts), end(counts), 0);
        for (auto& b : buckets) b.clear();
        highest_active = -1;
        for (auto v = 0; v < N; v++)
        {
            const auto h = heights[v];
            if (h == N) continue;
            counts[h]++;
            if (v != sink && excesses[v] > 0)
            {
                buckets[h].push_back(v);
                highest_active = max(highest_active, h);
            }
            cursors[v] = begin(adjacency_lists[v]);
        }
    }

    void push(const Edge e)
    {
        const auto residual = capacities[e.id] - flows[e.id];
        const auto delta = min(excesses[e.source], residual);
        flows[e.id] += delta;
        flows[e.reverse] -= delta;
        excesses[e.source] -= delta;
        if (excesses[e.target] == 0 && e.target != sink)
        {
            buckets[heights[e.target]].push_back(e.target);
            highest_active = max(highest_active, heights[e.target]);
        }
        excesses[e.target] += delta;
    }

    void gap(const int k)
    {
        for (auto v = 0; v < N; v++)
            if (heights[v] >= k && heights[v] != N)
            {
                counts[heights[v]]--;
                heights[v] = N;
            }
    }

    void relabel(const int v)
    {
        counts[heights[v]]--;
        heights[v] = N;
        for (const auto e : adjacency_lists[v])
            if (flows[e.id] < capacities[e.id])
                heights[v] = min(heights[v], heights[e.target]+1);
        if (heights[v] != N)
            counts[heights[v]]++;
    };

    void discharge(const int v)
    {
        auto& e = cursors[v];
        while (excesses[v] > 0 && heights[v] != N)
        {
            if (e == end(adjacency_lists[v]))
            {
                e = begin(adjacency_lists[v]);
                countdown_to_global_relabel--;
                if (countdown_to_global_relabel == 0)
                {
                    global_relabel();
                    break;
                }
                // if (counts[heights[v]] == 1) gap(heights[v]);
                else relabel(v);
            }
            else
            {
                const auto admissible = heights[e->target] < heights[v] &&
                    flows[e->id] < capacities[e->id];
                if (admissible) push(*e);
                else e++;
            }
        }
    };

    ll maxflow()
    {
        excesses = vector<ll>(N);
        heights = vector<int>(N);
        counts = vector<int>(N);
        buckets = vector<vector<int>>(N);
        cursors = vector<vector<Edge>::iterator>(N);

        for (const auto e : adjacency_lists[source])
        {
            excesses[source] = capacities[e.id];
            push(e);
        }
        global_relabel();

        while (highest_active >= 0)
        {
            if (!buckets[highest_active].empty())
            {
                const auto v = buckets[highest_active].back();
                buckets[highest_active].pop_back();
                if (heights[v] != N) // can happen through the gap heuristic
                    discharge(v);
            }
            else highest_active--;
        }
        return excesses[sink];
    }
};

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

    int n; cin >> n;

    auto network = Network();
    const auto source = 0, sink = 1;

    auto levelsIn = new int[17][int(1e5)]{};
    auto levelsOut = new int[17][int(1e5)]{};
    for (auto i = 0; i < n; i++)
    {
        auto v = levelsIn[0][i] = network.add_vertex();
        network.add_edge(source, v, 1);

        v = levelsOut[0][i] = network.add_vertex();
        network.add_edge(v, sink, 1);
    }

    for (auto k = 1; (1 << k) <= n; k++)
    {
        for (auto i = 0; i + (1 << k) <= n; i++)
        {
            auto v = levelsIn[k][i] = network.add_vertex();
            network.add_edge(levelsIn[k-1][i], v, n);
            network.add_edge(levelsIn[k-1][i+(1<<(k-1))], v, n);

            v = levelsOut[k][i] = network.add_vertex();
            network.add_edge(v, levelsOut[k-1][i], n);
            network.add_edge(v, levelsOut[k-1][i+(1<<(k-1))], n);
        }
    }

    int qs; cin >> qs;
    using evt = tuple<int, int, pair<int, int>>;
    vector<evt> evs;
    for (auto q = 0; q < qs; q++)
    {
        int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
        x1--; y1--; x2--; y2--;

        evs.push_back(evt{x1, +1, {y1, y2 + 1}});
        evs.push_back(evt{x2 + 1, -1, {y1, y2 + 1}});
    }
    evs.push_back(evt{0, -1, {0, n}});
    evs.push_back(evt{n, +1, {0, n}});
    sort(begin(evs), end(evs));

    auto insertRect = [&](int x0, int x1, int y0, int y1) {
        if (x0 == x1 || y0 == y1) {
            return;
        }
        int dx = 0, dy = 0;
        while (x0 + (1 << (dx + 1)) <= x1) {
            ++dx;
        }
        while (y0 + (1 << (dy + 1)) <= y1) {
            ++dy;
        }

        const auto u = network.add_vertex();
        network.add_edge(levelsIn[dx][x0], u, n);
        network.add_edge(levelsIn[dx][x1 - (1 << dx)], u, n);
        network.add_edge(u, levelsOut[dy][y0], n);
        network.add_edge(u, levelsOut[dy][y1 - (1 << dy)], n);
    };

    map<int, pair<int, int>> ranges;
    for (auto& e : evs)
    {
        int x = get<0>(e);
        auto& range = get<2>(e);
        if (get<1>(e) == -1) {
            ranges[range.first] = {range.second, x};
        } else {
            auto it = ranges.upper_bound(range.first);
            --it;
            pair<int, int> outer;
            outer.first = it->first;
            while (it != ranges.end() && it->first < range.second) {
                insertRect(it->second.second, x, it->first, it->second.first);
                outer.second = it->second.first;
                auto jt = it;
                it++;
                ranges.erase(jt);
            }
            if (outer.first < range.first) {
                ranges[outer.first] = {range.first, x};
            }
            if (range.second < outer.second) {
                ranges[range.second] = {outer.second, x};
            }
        }
    }

    cout << network.maxflow() << endl;
}

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+7;
int n,q,tot=1,cnt,s,t;
bool mp[N][N];
int h[N],id[N<<2][2];
int dep[N<<3],head[N<<3],to[N<<8],w[N<<8],nxt[N<<8];
inline void link(int a,int b,int c){
	nxt[++tot]=head[a]; to[head[a]=tot]=b; w[tot]=c;
	nxt[++tot]=head[b]; to[head[b]=tot]=a; w[tot]=0;
}
void build(int p,int l,int r){
	id[p][0]=++cnt; id[p][1]=++cnt;
	if(l==r) return link(s,id[p][0],1),link(id[p][1],t,1);
	const int mid=l+r>>1;
	build(p<<1,l,mid); build(p<<1|1,mid+1,r);
	link(id[p<<1][0],id[p][0],N); link(id[p<<1|1][0],id[p][0],N);
	link(id[p][1],id[p<<1][1],N); link(id[p][1],id[p<<1|1][1],N);
}
void insert(int p,int opt,int Id,int l,int r,int L,int R){
	if(L>=l&&R<=r) return opt?link(Id,id[p][opt],N):link(id[p][opt],Id,N);
	const int mid=L+R>>1;
	if(l<=mid) insert(p<<1,opt,Id,l,r,L,mid);
	if(r>mid) insert(p<<1|1,opt,Id,l,r,mid+1,R);
}
int dinic(int x,int flow){
	if(x==t) return flow;
	int res=flow,k;
	for(int i=head[x];i&&res;i=nxt[i]) if(w[i]&&dep[to[i]]==dep[x]+1){
		k=dinic(to[i],min(res,w[i]));
		res-=k; w[i]-=k; w[i^1]+=k;
		if(!k) dep[to[i]]=0;
	}
	return flow-res;
}
inline bool bfs(){
	memset(dep,0,sizeof(dep));
	queue<int> q; q.push(s); dep[s]=1;
	while(!q.empty()){
		int x=q.front(); q.pop();
		for(int i=head[x];i;i=nxt[i]) if(w[i]&&!dep[to[i]]){
			dep[to[i]]=dep[x]+1;
			q.push(to[i]);
			if(to[i]==t) return 1;
		}
	}
	return 0;
}
int main(){
	scanf("%d%d",&n,&q); s=++cnt; t=++cnt;
	for(int i=1,x,y,X,Y;i<=q;++i){
		scanf("%d%d%d%d",&x,&y,&X,&Y);
		for(int j=x;j<=X;++j) for(int k=y;k<=Y;++k) mp[j][k]=1;
	}
	for(int i=1;i<=n;++i) mp[n+1][i]=1;
	build(1,1,n);
	for(int i=1;i<=n+1;++i){
		vector<pair<int,int> > ve;
		for(int j=1;j<=n;++j){
			if(!mp[i][j]) ++h[j];
			else{
				if(h[j]) ve.push_back(make_pair(j,h[j]));
				h[j]=0;
			}
		}
		for(int l=0,r=1;l<ve.size();l=r++){
			while(r<ve.size()&&ve[r].first==ve[r-1].first+1&&ve[r].second==ve[r-1].second) ++r;
			int tl=ve[l].first,tr=ve[r-1].first,L=i-ve[l].second,R=i-1; ++cnt;
			insert(1,0,cnt,tl,tr,1,n); insert(1,1,cnt,L,R,1,n);
		}
	} int ans=0;
	while(bfs()) ans+=dinic(s,N);
	return printf("%d\n",ans),0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=10010,inf=1061109567;
struct Tree{
	int l,r;
}t[N<<2];
struct node{
	int to,flow,nxt;
}h[5000100];
int n,m,S,T,cnt,D,tot,head[500100],he[N],dep[500100];
bool mp[N][N];
vector< pair<int,int> >ve;
int read()
{
	int aa=0,bb=1;char cc=getchar();
	while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();}
	while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();}
	return aa*bb;
}
void add(int x,int y,int flow)
{
	h[++tot].to=y;
	h[tot].flow=flow;
	h[tot].nxt=head[x];
	head[x]=tot;
}
void link(int x,int y,int flow){add(x,y,flow);add(y,x,0);/*cout<<"!!"<<x<<" "<<y<<" "<<flow<<endl;*/}
void build(int x,int l,int r)
{
	t[x].l=l;t[x].r=r;
	if(l==r) return cnt=max(cnt,x),void();
	int mid=l+r>>1;
	build(x<<1,l,mid);build(x<<1|1,mid+1,r);
}
void query(int x,int l,int r,int id,int opt)
{
	if(l<=t[x].l&&t[x].r<=r){
		if(opt) link(id,x+D,t[x].r-t[x].l+1);
		else link(x,id,t[x].r-t[x].l+1);
		return;
	}
	int mid=t[x].l+t[x].r>>1;
	if(l<=mid) query(x<<1,l,r,id,opt);
	if(r>mid) query(x<<1|1,l,r,id,opt);
}
void Link(int x,int opt)
{
	if(t[x].l==t[x].r){
		if(opt) link(x+D,T,1);
		return;
	}
	Link(x<<1,opt);Link(x<<1|1,opt);
	int mid=t[x].l+t[x].r>>1;
	if(opt) link(x+D,(x<<1)+D,mid-t[x].l+1),link(x+D,(x<<1|1)+D,t[x].r-mid);
	else link(x,x<<1,mid-t[x].l+1),link(x,x<<1|1,t[x].r-mid);
}
bool bfs()
{
	queue<int>q;memset(dep,0,sizeof(dep));dep[S]=1;q.push(S);
	while(q.size()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=h[i].nxt){
			int y=h[i].to;
			if(h[i].flow>0&&!dep[y]) dep[y]=dep[x]+1,q.push(y);
		}
	}
	return dep[T];
}
int dfs(int x,int flow)
{
	if(x==T||flow<=0) return flow;
	int res=flow;
	for(int i=head[x];i;i=h[i].nxt){
		int y=h[i].to;
		if(h[i].flow<=0||dep[y]!=dep[x]+1) continue;
		int tmp=dfs(y,min(res,h[i].flow));
		if(!tmp) dep[y]=0;
		h[i].flow-=tmp;h[i^1].flow+=tmp;res-=tmp;
		if(!res) break;
	}
	return flow-res;
}
int dinic()
{
	int ans=0;
	while(bfs()) ans+=dfs(S,inf);
	return ans;
}
int main()
{
	n=read();m=read();tot=1;int x,y,xx,yy;
	for(int i=1;i<=m;i++){
		x=read();y=read();xx=read();yy=read();
		for(int j=x;j<=xx;j++) for(int k=y;k<=yy;k++) mp[j][k]=1;
	}
	for(int i=1;i<=n;i++) mp[n+1][i]=1;
	build(1,1,n);D=cnt;cnt+=cnt;S=++cnt;T=++cnt;Link(1,0);Link(1,1);link(S,1,n);
	for(int i=1;i<=n+1;i++){
		ve.clear();
		for(int j=1;j<=n;j++){
			if(!mp[i][j]) he[j]++;
			else if(he[j]) ve.push_back(make_pair(j,he[j])),he[j]=0;
		}
		int p=0;
		for(int j=0;j<ve.size();j++){
			p=j;
			while(p+1<ve.size()&&ve[p].first+1==ve[p+1].first&&ve[p].second==ve[p+1].second) p++;
			cnt++;
			query(1,ve[j].first,ve[p].first,cnt,0);
			query(1,i-ve[j].second,i-1,cnt,1);
			j=p;
		}
	}
	printf("%d\n",dinic());
	return 0;
}

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>

using namespace std;
typedef long long LL;

#define re register
#define gc getchar
#define digit(x) (x >= '0' && x <= '9')

int read()
{
	int x = 0, f = 1; char c = gc();
	while (!digit(c)) {if (c == '-') f = -1; c = gc();}
	while (digit(c)) x = (x << 3) + (x << 1) + c - '0', c = gc();
	return x * f;
}

const int N = 2e5 + 5;
const int inf = 0x3f3f3f3f;

int n, q;
int S, T;
vector<int> v[2];

namespace Dinic
{
	int cnte;
	int h[N << 3];
	struct Edge
	{
		int val, to, nxt;
		Edge(){}
		Edge(int V, int T, int N){val = V; to = T; nxt = N;}
	}E[N << 4];

	void Init()
	{
		cnte = -1;
		memset(h, -1, sizeof h);
	}
	void Add_edge(int u, int v, int val)
	{
		E[++cnte] = Edge(val, v, h[u]), h[u] = cnte;
		E[++cnte] = Edge(0, u, h[v]), h[v] = cnte;
	}

	queue<int> Q;
	int dep[N << 3];
	bool Bfs(int s, int t)
	{
		while (!Q.empty()) Q.pop();
		memset(dep, -1, sizeof dep);
		memset(dep, -1, sizeof dep);
		Q.push(s);
		dep[s] = 0;
		while (!Q.empty())
		{
			int u = Q.front(); Q.pop();
			for (re int i = h[u]; ~i; i = E[i].nxt)
			{
				int v = E[i].to;
				if (~dep[v] || !E[i].val) continue;
				Q.push(v);
				dep[v] = dep[u] + 1;
				if (v == t)
					return true;
			}
		}
		return false;
	}

	int cur[N << 3];
	inline int Dfs(int u, int t, int flow)
	{
		if (u == t || !flow) return flow;

		int used = 0;
		for (re int i = cur[u]; ~i; i = E[i].nxt)
		{
			int v = E[i].to;
			if (dep[v] == dep[u] + 1)
			{
				int tmp = Dfs(v, t, min(flow, E[i].val));
				E[i].val -= tmp;
				E[i ^ 1].val += tmp;
				used += tmp;
				flow -= tmp;
				if (!flow) break;
			}
		}
		if (!used) dep[u] = -1;
		return used;
	}

	int Dinic(int s, int t)
	{
		int res = 0;
		while (Bfs(s, t))
		{
			for (re int i = s; i <= t; i++)
				cur[i] = h[i];
			res += Dfs(s, t, inf);
		}
		return res;
	}
}

namespace Segment_Tree
{
	#define lch (x << 1)
	#define rch (x << 1 | 1)
	struct Node
	{
		int l, r;
		int num;
	}Tr[2][N << 4];
	int cnt = 0;
	void Build_Tree(int x, int l, int r, int o)
	{
		Tr[o][x].l = l, Tr[o][x].r = r, Tr[o][x].num = ++cnt;
		if (l == r) return ;
		int mid = (l + r) >> 1;
		Build_Tree(lch, l, mid, o);
		Build_Tree(rch, mid + 1, r, o);
	}
	void Build_Edge(int x, int o)
	{
		if (Tr[o][x].l == Tr[o][x].r)
		{
			if (!o) Dinic::Add_edge(S, Tr[o][x].num, 1);
			else Dinic::Add_edge(Tr[o][x].num, T, 1);
		}
		else
		{
			if (!o)
			{
				Dinic::Add_edge(Tr[o][lch].num, Tr[o][x].num, inf);
				Dinic::Add_edge(Tr[o][rch].num, Tr[o][x].num, inf);
			}
			else
			{
				Dinic::Add_edge(Tr[o][x].num, Tr[o][lch].num, inf);
				Dinic::Add_edge(Tr[o][x].num, Tr[o][rch].num, inf);
			}

			Build_Edge(lch, o);
			Build_Edge(rch, o);
		}
	}

	void Get_Segment(int x, int l, int r, int o)
	{
		if (l <= Tr[o][x].l && Tr[o][x].r <= r)
		{
			v[o].push_back(Tr[o][x].num);
			return ;
		}
		if (l > Tr[o][x].r || r < Tr[o][x].l)
			return ;
		Get_Segment(lch, l, r, o);
		Get_Segment(rch, l, r, o);
	}
	void Build_Edge_Area(int x1, int y1, int x2, int y2)
	{
		if (x1 > x2 || y1 > y2) return ;
		v[0].clear(); v[1].clear();
		Get_Segment(1, x1, x2, 0);
		Get_Segment(1, y1, y2, 1);
		for (re int i = 0; i < v[0].size(); i++)
			for (re int j = 0; j <v[1].size(); j++)
				Dinic::Add_edge(v[0][i], v[1][j], inf);
	}
}
struct Node
{
	int l, r;
	Node(){}
	Node(int L, int R){l = L; r = R;}
};
vector<Node> vec[N << 2][2];
int left[N << 2];
int main()
{
	Dinic::Init();
	n = read(); q = read();
	Segment_Tree::Build_Tree(1, 1, n, 0);
	Segment_Tree::Build_Tree(1, 1, n, 1); T = Segment_Tree::cnt + 1;
	Segment_Tree::Build_Edge(1, 0);
	Segment_Tree::Build_Edge(1, 1);

	for (re int i = 1; i <= q; i++)
	{
		int x1 = read(), y1 = read(), x2 = read(), y2 = read();
		vec[x1][0].push_back(Node(y1, y2));
		vec[x2][1].push_back(Node(y1, y2));
	}
	vec[n + 1][0].push_back(Node(1, n));
	for (re int i = 1; i <= n + 1; i++)
	{
		for (re int j = 0; j < vec[i][0].size(); j++)
		{
			int las = vec[i][0][j].l;
			for (re int k = vec[i][0][j].l + 1; k <= vec[i][0][j].r; k++)
				if (left[k] != left[k - 1])
					Segment_Tree::Build_Edge_Area(left[k - 1] + 1, las, i - 1, k - 1), las = k;
			Segment_Tree::Build_Edge_Area(left[vec[i][0][j].r] + 1, las, i - 1, vec[i][0][j].r);
		}
		for (re int j = 0; j < vec[i][1].size(); j++)
			for (re int k = vec[i][1][j].l; k <= vec[i][1][j].r; k++)
				left[k] = i;
	}
	int ans = Dinic::Dinic(S, T);
	printf("%d\n", ans);
	return 0;
}

#include<bits/stdc++.h>
#define maxn 10005
#define maxp 5000005
#define maxm 5000005
#define inf 0x3f3f3f3f
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char Start;

int n,q;
vector<pair<int,int> >in[maxn],ot[maxn];
int rt[maxn],lc[maxp],rc[maxp],ad[maxp],tot;
int buf[maxp],info[maxp],Prev[maxm],to[maxp],cap[maxp],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
void Line(int u,int v,int c){  Node(u,v,c),Node(v,u,0);}
int h[maxp],S,T;

void upd(int u){
	if(!ad[lc[u]]) Line(u,lc[u],inf);
	if(!ad[rc[u]]) Line(u,rc[u],inf);
}

void Build(int &u,int l,int r){
	if(l==r){
		u = l;
		return;
	}
	u = ++tot;
	int m = l+r>>1;
	Build(lc[u],l,m) , Build(rc[u],m+1,r);
	upd(u);
}

void add(int &u,int l,int r,int ql,int qr,int v){
	if(ql>r||l>qr||ql>qr) return;
	if(l == r){
		ad[u] += v;
		return;
	}
	lc[++tot] = lc[u] , rc[tot] = rc[u] , ad[tot] = ad[u];
	u = tot;
	if(ql<=l&&r<=qr) return (void)(ad[u] += v , upd(u));
	int m = l+r>>1;
	add(lc[u],l,m,ql,qr,v) , add(rc[u],m+1,r,ql,qr,v);
	upd(u);
}

bool BFS(){
	static int q[maxp],L,R;
	memset(h,-1,sizeof(int) * (tot+1));
	h[q[L=R=0] = T] = 0;
	for(int u;L<=R;){
		u=q[L++];
		for(int i=info[u],v;i;i=Prev[i]) if(cap[i^1] && h[v=to[i]] == -1){
			h[v] = h[u] + 1;
			q[++R] = v;
		}
	}
	return h[S] != -1;
}

int aug(int u,int mx){
	if(u == T) return mx;
	int st = mx , inc;
	for(int &i=info[u],v;i;i=Prev[i]) if(cap[i] && h[v=to[i]] + 1 == h[u] && (inc = aug(v , min(cap[i] , st)))){
		st -= inc , cap[i] -= inc , cap[i^1] += inc;
		if(!st) return mx - st;
	}
	return mx - st;
}

char End;

int main(){
//	freopen("1.in","r",stdin);

	scanf("%d%d",&n,&q);
	rep(i,1,q){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		in[a].push_back(make_pair(b,d));
		ot[c+1].push_back(make_pair(b,d));
	}
	tot = 2 * n;
	S=++tot,T=++tot;
	Build(rt[0],1,n);
	rep(i,1,n) Line(i,T,1) , Line(S,i+n,1);
	rep(i,1,n){
		rt[i] = rt[i-1];
		for(auto u:in[i]) add(rt[i] , 1 , n , u.first , u.second , 1);
		for(auto u:ot[i]) add(rt[i] , 1 , n , u.first , u.second , -1);
		if(!ad[rt[i]]) Line(i+n,rt[i],inf);
	}
	int stm = 0;
	for(memcpy(buf,info,sizeof(int) * (tot+1));BFS();)
		stm += aug(S,inf) , memcpy(info,buf,sizeof(int) * (tot+1));
	printf("%d\n",stm);
}

#include<iostream>
#include<vector>
#include<deque>
#include<functional>
#include<map>
using namespace std;

//----------------------------------------------------------------
//OMLBEGIN: Flow Network
#include<vector>
#include<deque>
#include<functional>
#include<tuple>
#include<climits>
using namespace std;

struct Network {
	struct Node;

	struct Edge {
		int index;
		Node *u, *v;
		int cap, flow;

		Node* from(Node* pos) {
			if(pos == u) return v;
			return u;
		}
		void addFlow(Node* pos, int toAdd) {
			if(pos == u) flow += toAdd;
			else flow -= toAdd;
		}
		int getCap(Node* pos) {
			if(pos == u) return cap-flow;
			return flow;
		}
	};
	struct Node {
		int index;
		vector<Edge*> conn;
	};
	int n = 0, m = 0;
	deque<Node> nodes;
	deque<Edge> edges;

	Node* addNode() {
		nodes.push_back({n++});
		return &nodes.back();
	}
	Edge* addEdge(Node* u, Node* v, int cap = 1, int flow = 0) {
		edges.push_back({m++, u, v, cap, flow});
		u->conn.push_back(&edges.back());
		v->conn.push_back(&edges.back());
		return &edges.back();
	}

	int dinic(Node* source, Node* sink) {
		int result = 0;
		while(1) {
			//First divide into layers
			vector<int> level(n, -1);
			{
				deque<tuple<int, Node*> > que(1, make_tuple(0, source) );
				level[source->index] = 0;

				while(que.size() > 0) {
					int lev; Node* cur;
					tie(lev, cur) = que.front();
					que.pop_front();

					for(auto ep : cur->conn) if(ep->getCap(cur) > 0) {
						Node* next = ep->from(cur);
						if(level[next->index] == -1) {
							level[next->index] = lev+1;
							que.push_back(make_tuple(lev+1, next));
						}
					}
				}

				if(level[sink->index] == -1)
					return result;
			}

			//Now perform dfs to saturate the edges
			vector<int> connIndex(n, 0);

			function<int (Node*, int)> dfs = [&](Node* cur, int cap) {
				if(cur == sink)
					return cap;

				for(int& i = connIndex[cur->index]; i < (int)cur->conn.size(); i++) {
					Edge* edge = cur->conn[i]; Node* next = edge->from(cur);
					if(edge->getCap(cur) > 0 && level[cur->index] < level[next->index]) {
						int ret = dfs(next, min(cap, edge->getCap(cur) ) );
						if(ret > 0) {
							edge->addFlow(cur, ret);
							return ret;
						}
					}
				}
				return 0;
			};
			int ret;
			do {
				ret = dfs(source, INT_MAX);
				result += ret;
			}while(ret > 0);
		}
	}
};
//OMLEND: Flow Network
//----------------------------------------------------------------

void getBinaryTreeIndices(vector<int>& out, int l, int r, int pos, int lb, int rb) {
	if(l <= lb && rb <= r) {
		out.push_back(pos);
		return;
	}

	int mid = (lb + rb) / 2;
	if(l < mid)
		getBinaryTreeIndices(out, l, r, pos*2+1, lb, mid);
	if(mid < r)
		getBinaryTreeIndices(out, l, r, pos*2+2, mid, rb);
}

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

	int n, q;
	cin >> n >> q;

	Network net;
	vector<Network::Node*> intree, outtree;
	int treen = 1;
	while(treen < n)
		treen *= 2;

	//Initialize the binary trees for rows and columns
	{
		for(int i=0;i<treen*2-1;i++)
			intree.push_back(net.addNode());
		for(int i=0;i<treen*2-1;i++)
			outtree.push_back(net.addNode());

		vector<int> size(2*treen-1, 1);

		for(int i=treen-2;i>=0;i--) {
			net.addEdge(intree[i], intree[2*i+1], size[2*i+1]);
			net.addEdge(intree[i], intree[2*i+2], size[2*i+2]);
			net.addEdge(outtree[2*i+1], outtree[i], size[2*i+1]);
			net.addEdge(outtree[2*i+2], outtree[i], size[2*i+2]);

			size[i] = size[2*i+1] + size[2*i+2];
		}

	}

	//Now process the deleted rectangles
	{
		struct Rect {
			int xl, xr, yl, yr;
		};

		deque<Rect> rects(1, {0, n, 0, 0});

		vector<vector<pair<int, int> > > added(n+1), removed(n+1);

		for(int i=0;i<q;i++) {
			int x1, x2, y1, y2;
			cin >> x1 >> y1 >> x2 >> y2;
			x1--, y1--;

			added[y1].push_back({x1, x2});
			removed[y2].push_back({x1, x2});
		}

		map<int, Rect*> active;
		active.insert({n, &rects[0]});

		for(int y=0;y<n;y++) {

			for(auto cseg : removed[y]) {
				auto curl = *active.lower_bound(cseg.first);
				auto curr = *active.lower_bound(cseg.second);

				curl.second->yr = y;
				curr.second->yr = y;
				active.erase(curl.first);
				active.erase(curr.first);

				rects.push_back({curl.second->xl, curr.second->xr, y, 0});
				active[rects.back().xr] = &rects.back();
			}

			for(auto cseg : added[y]) {
				auto cur = *active.lower_bound(cseg.second);

				cur.second->yr = y;
				active.erase(cur.first);

				rects.push_back({cur.second->xl, cseg.first, y, 0});
				active[rects.back().xr] = &rects.back();
				rects.push_back({cseg.second, cur.second->xr, y, 0});
				active[rects.back().xr] = &rects.back();
			}

		}

		for(auto cur : active)
			cur.second->yr = n;

		//Add the rectangles as nodes to our network
		for(auto rect : rects) if(rect.xr > rect.xl && rect.yr > rect.yl) {
			vector<int> ininds, outinds;
			getBinaryTreeIndices(ininds, rect.xl, rect.xr, 0, 0, treen);
			getBinaryTreeIndices(outinds, rect.yl, rect.yr, 0, 0, treen);

			Network::Node* cnode = net.addNode();

			for(auto c : ininds)
				net.addEdge(intree[c], cnode, n);
			for(auto c : outinds)
				net.addEdge(cnode, outtree[c], n);
		}
	}

	//And output the result
	cout << net.dinic(intree[0], outtree[0]) << '\n';

	return 0;
}

#include <cstdio>
#include <memory.h>
#include <cassert>
#include <vector>
using namespace std;

const int N = 2000500;

struct edge {
    int t, f, c;
};

int ept;
edge edges[N];

vector<int> E[N];

void add_edge(int a, int b, int c) {
    edges[ept++] = {b, 0, c};
    edges[ept++] = {a, 0, 0};
    E[a].push_back(ept - 2);
    E[b].push_back(ept - 1);
}

int D[N];
int Q[N];
int used[N];
int curu = 0;

int pt[N];

bool BFS(int s, int t) {
    int lpt = 0, rpt = 0;
    Q[rpt++] = s;
    D[s] = 0;
    used[s] = ++curu;
    while (lpt < rpt) {
        int x = Q[lpt++];
        pt[x] = 0;
        for (int e : E[x]) {
            auto& ed = edges[e];
            if (used[ed.t] == curu || ed.f == ed.c)
                continue;
            used[ed.t] = curu;
            D[ed.t] = D[x] + 1;
            Q[rpt++] = ed.t;
        }
    }
    return used[t] == curu;
}

const int inf = 1e9 + 42;

int push(int x, int t, int cur = inf) {
    if (x == t) {
        return cur;
    }
    int sum = 0;
    for (; pt[x] != (int)E[x].size(); pt[x]++) {
        auto &ed = edges[E[x][pt[x]]], &red = edges[E[x][pt[x]] ^ 1];
        if (ed.f == ed.c || D[ed.t] != D[x] + 1)
            continue;
        int rem = ed.c - ed.f;
        int pushed = push(ed.t, t, min(cur, rem));
        if (pushed) {
            ed.f += pushed;
            red.f -= pushed;
            cur -= pushed;
            sum += pushed;
        }
        if (cur == 0)
            break;
    }
    return sum;
}

int ver = 0;

const int M = 10500;
bool F[M][M];

int s, t;
vector<int> L, R;
vector<int> TL, TR;

void build(int l, int r, int v, int up, vector<int>& A, vector<int>& T) {
    T[v] = ver++;
    if (l == r) {
        A[l] = T[v];
        return;
    }
    build(l, (l + r) / 2, 2 * v, up, A, T);
    build((l + r) / 2 + 1, r, 2 * v + 1, up, A, T);
    if (up) {
        add_edge(T[2 * v], T[v], inf);
        add_edge(T[2 * v + 1], T[v], inf);
    } else {
        add_edge(T[v], T[2 * v], inf);
        add_edge(T[v], T[2 * v + 1], inf);
    }

}

void add(int l, int r, int L, int R, int v, int up, int x, vector<int>& T) {
    if (l <= L && R <= r) {
        if (up) {
            add_edge(T[v], x, inf);
        } else {
            add_edge(x, T[v], inf);
        }
        return;
    }
    if (r < L || l > R) {
        return;
    }
    add(l, r, L, (L + R) / 2, 2 * v, up, x, T);
    add(l, r, (L + R) / 2 + 1, R, 2 * v + 1, up, x, T);
}

int n;

void add_rect(int l1, int l2, int r1, int r2) {
    //fprintf(stderr, "adding rect [%d, %d] [%d, %d]\n", l1, l2, r1, r2);
    int c = ver++;
    add(l1, l2, 0, n - 1, 1, true, c, TL);
    add(r1, r2, 0, n - 1, 1, false, c, TR);
}

int main() {
    int q;
    scanf("%d %d", &n, &q);
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        --x1, --y1, --x2, --y2;
        swap(x1, y1);
        swap(x2, y2);
        for (int y = y1; y <= y2; y++) {
            memset(F[y] + x1, 1, (x2 - x1 + 1) * sizeof(bool));
        }
    }
    int s = ver++;
    int t = ver++;
    L.resize(n);
    R.resize(n);
    TL.resize(4 * n);
    TR.resize(4 * n);
    build(0, n - 1, 1, true, L, TL);
    build(0, n - 1, 1, false, R, TR);
    for (int i = 0; i < n; i++) {
        add_edge(s, L[i], 1);
        add_edge(R[i], t, 1);
    }

    for (int i = 0; i < n; i++) {
        F[i][n] = true;
        F[n][i] = true;
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (!F[i][j]) {
                int k = j;
                while (!F[i][k])
                    ++k;
                --k;
                int last = i;
                for (int z = i; z < n; z++) {
                    bool any = false;
                    for (int r = j; r <= k; r++) {
                        if (F[z][r]) {
                            any = true;
                            break;
                        }
                    }
                    if (any)
                        break;
                    last = z;
                }
                for (int z = i; z <= last; z++)
                    memset(F[z] + j, 1, (k - j + 1) * sizeof(bool));
                add_rect(i, last, j, k);
            }
        }
    }

    int flow = 0;
    while (true) {
        if (!BFS(s, t))
            break;
        int pushed = push(s, t);
        assert(pushed > 0);
        flow += pushed;
    }
    fprintf(stderr, "ver = %d, ept = %d\n", ver, ept);
    printf("%d\n", flow);
    //while(true);
}

// powered by c++11
// by Isaunoya
#include <bits/stdc++.h>

#define rep(i, x, y) for (register int i = (x); i <= (y); ++i)
#define Rep(i, x, y) for (register int i = (x); i >= (y); --i)

using namespace std;
using db = double;
using ll = long long;
using uint = unsigned int;
using ull = unsigned long long;

#define pii pair<int, int>
#define fir first
#define sec second

template <class T>

void cmax(T& x, const T& y) {
  if (x < y) x = y;
}

template <class T>

void cmin(T& x, const T& y) {
  if (x > y) x = y;
}

#define all(v) v.begin(), v.end()
#define sz(v) ((int)v.size())
#define pb emplace_back

template <class T>

void sort(vector<T>& v) {
  sort(all(v));
}

template <class T>

void reverse(vector<T>& v) {
  reverse(all(v));
}

template <class T>

void unique(vector<T>& v) {
  sort(all(v)), v.erase(unique(all(v)), v.end());
}

void reverse(string& s) { reverse(s.begin(), s.end()); }

const int io_size = 1 << 23 | 233;
const int io_limit = 1 << 22;
struct io_in {
  char ch;
#ifndef __WIN64
  char getchar() {
    static char buf[io_size], *p1 = buf, *p2 = buf;

    return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, io_size, stdin), p1 == p2) ? EOF : *p1++;
  }
#endif
  io_in& operator>>(char& c) {
    for (c = getchar(); isspace(c); c = getchar())
      ;

    return *this;
  }
  io_in& operator>>(string& s) {
    for (s.clear(); isspace(ch = getchar());)
      ;

    if (!~ch) return *this;

    for (s = ch; !isspace(ch = getchar()) && ~ch; s += ch)
      ;

    return *this;
  }

  io_in& operator>>(char* str) {
    char* cur = str;
    while (*cur) *cur++ = 0;

    for (cur = str; isspace(ch = getchar());)
      ;
    if (!~ch) return *this;

    for (*cur = ch; !isspace(ch = getchar()) && ~ch; *++cur = ch)
      ;

    return *++cur = 0, *this;
  }

  template <class T>

  void read(T& x) {
    bool f = 0;
    while ((ch = getchar()) < 48 && ~ch) f ^= (ch == 45);

    x = ~ch ? (ch ^ 48) : 0;
    while ((ch = getchar()) > 47) x = x * 10 + (ch ^ 48);
    x = f ? -x : x;
  }

  io_in& operator>>(int& x) { return read(x), *this; }

  io_in& operator>>(ll& x) { return read(x), *this; }

  io_in& operator>>(uint& x) { return read(x), *this; }

  io_in& operator>>(ull& x) { return read(x), *this; }

  io_in& operator>>(db& x) {
    read(x);
    bool f = x < 0;
    x = f ? -x : x;
    if (ch ^ '.') return *this;

    double d = 0.1;
    while ((ch = getchar()) > 47) x += d * (ch ^ 48), d *= .1;
    return x = f ? -x : x, *this;
  }
} in;

struct io_out {
  char buf[io_size], *s = buf;
  int pw[233], st[233];

  io_out() {
    set(7);
    rep(i, pw[0] = 1, 9) pw[i] = pw[i - 1] * 10;
  }

  ~io_out() { flush(); }

  void io_chk() {
    if (s - buf > io_limit) flush();
  }

  void flush() { fwrite(buf, 1, s - buf, stdout), fflush(stdout), s = buf; }

  io_out& operator<<(char c) { return *s++ = c, *this; }

  io_out& operator<<(string str) {
    for (char c : str) *s++ = c;
    return io_chk(), *this;
  }

  io_out& operator<<(char* str) {
    char* cur = str;
    while (*cur) *s++ = *cur++;
    return io_chk(), *this;
  }

  template <class T>

  void write(T x) {
    if (x < 0) *s++ = '-', x = -x;

    do {
      st[++st[0]] = x % 10, x /= 10;
    } while (x);

    while (st[0]) *s++ = st[st[0]--] ^ 48;
  }

  io_out& operator<<(int x) { return write(x), io_chk(), *this; }

  io_out& operator<<(ll x) { return write(x), io_chk(), *this; }

  io_out& operator<<(uint x) { return write(x), io_chk(), *this; }

  io_out& operator<<(ull x) { return write(x), io_chk(), *this; }

  int len, lft, rig;

  void set(int _length) { len = _length; }

  io_out& operator<<(db x) {
    bool f = x < 0;
    x = f ? -x : x, lft = x, rig = 1. * (x - lft) * pw[len];
    return write(f ? -lft : lft), *s++ = '.', write(rig), io_chk(), *this;
  }
} out;

int n, m, s, t;
const int maxn = 3e4 + 43;
const int inf = 1e9;

namespace Dinic {
int head[maxn << 6], cur[maxn << 6];
struct List {
  int v, nxt, w;
} e[maxn << 7];
int cnt = 1;
void add(int u, int v, int w) {
  e[++cnt] = { v, head[u], w }, head[u] = cnt;
  e[++cnt] = { u, head[v], 0 }, head[v] = cnt;
}

int d[maxn << 6];
bool bfs(int s) {
  memset(d, 0, sizeof(d));
  queue<int> q;
  d[s] = 1, q.push(s);
  while (!q.empty()) {
    int u = q.front();
    q.pop();
    for (int i = head[u]; i; i = e[i].nxt) {
      int& v = e[i].v;
      if (!d[v] && e[i].w) {
        d[v] = d[u] + 1;
        q.push(v);
      }
    }
  }
  return d[t];
}

int dfs(int u, int flow) {
  if (u == t) return flow;
  int fl = 0;
  for (int i = cur[u]; i; i = e[i].nxt) {
    int& v = e[i].v;
    cur[u] = i;
    if (d[v] == d[u] + 1 && e[i].w) {
      int dis = dfs(v, min(flow, e[i].w));
      if (dis) {
        e[i].w -= dis;
        e[i ^ 1].w += dis;
        flow -= dis;
        fl += dis;
        if (!flow) return fl;
      }
    }
  }
  return fl;
}

int dinic() {
  int ans = 0;
  while (bfs(s)) {
    memcpy(cur, head, sizeof(head));
    ans += dfs(s, inf);
  }
  return ans;
}
}  // namespace Dinic

using Dinic ::add;

namespace SegMentTree {
int id[maxn << 2];
int tag[maxn << 2];
int cnt;

void pushup(int p) {
  id[p] = ++cnt;
  if (!tag[p << 1]) add(id[p], id[p << 1], inf);
  if (!tag[p << 1 | 1]) add(id[p], id[p << 1 | 1], inf);
}

void build(int p, int l, int r) {
  if (l == r) {
    id[p] = (n + l);
    return;
  }
  int mid = l + r >> 1;
  build(p << 1, l, mid);
  build(p << 1 | 1, mid + 1, r);
  pushup(p);
}

void upd(int p, int a, int b, int l, int r, int v) {
  if (a <= l && r <= b) {
    tag[p] += v;
    return;
  }
  int mid = l + r >> 1;
  if (a <= mid) upd(p << 1, a, b, l, mid, v);
  if (b > mid) upd(p << 1 | 1, a, b, mid + 1, r, v);
  pushup(p);
}
}  // namespace SegMentTree

using SegMentTree ::build;
using SegMentTree ::upd;

struct chg {
  int l, r, val;
};
vector<chg> v[maxn];

signed main() {
  // code begin.
  in >> n >> m;
  s = 2 * n + 1, t = 2 * n + 2, SegMentTree ::cnt = 2 * n + 2;
  build(1, 1, n);
  while (m--) {
    int x1, y1, x2, y2;
    in >> x1 >> y1 >> x2 >> y2;
    v[x1].push_back({ y1, y2, 1 });
    v[x2 + 1].push_back({ y1, y2, -1 });
  }
  rep(i, 1, n) add(s, i, 1), add(i + n, t, 1);
  rep(i, 1, n) {
    for (auto x : v[i]) {
      int l = x.l, r = x.r, v = x.val;
      upd(1, l, r, 1, n, v);
    }
    if (!SegMentTree ::tag[1]) add(i, SegMentTree ::id[1], inf);
  }
  int ans = Dinic ::dinic();
  out << ans << '\n';
  return 0;
  // code end.
}

#include <cstdio>
#include <cassert>
#include <map>
#include <algorithm>
#include <vector>
template < int xn, int xe > struct flow
{
	int head[xn], next[xe], to[xe], fl[xe], N, E, dep[xn], q[xn + 1];
	inline void add(int u, int v, int f)
	{
		next[E] = head[u], to[E] = v, fl[E] = f, head[u] = E++;
		next[E] = head[v], to[E] = u, fl[E] = 0, head[v] = E++;
	}
	void clear(int n)
	{
		N = n, E = 0;
		for (int i = 0; i < N; i++)
			head[i] = -1;
	}
	int BFS()
	{
		for (int i = 0; i < N; i++)
			dep[i] = 0;
		dep[0] = 1;
		q[1] = 0;
		int *H = q, *T = q + 1, u;
		while (H < T)
			for (int e = head[u = *++H]; ~e; e = next[e])
				if (fl[e] && !dep[to[e]])
				{
					dep[*++T = to[e]] = dep[u] + 1;
					if (*T == N - 1)
						return 1;
				}
		return 0;
	}
	int DFS(int p, int f)
	{
		if (p == N - 1)
			return f;
		int u = 0, y;
		for (int e = head[p]; ~e; e = next[e])
			if (fl[e] && dep[to[e]] == dep[p] + 1)
				if (y = DFS(to[e], std::min(f, fl[e])))
				{
					u += y;
					f -= y;
					fl[e] -= y;
					fl[e ^ 1] += y;
					if (!f)
						return u;
				}
		if (!u)
			dep[p] = 0;
		return u;
	}
	int operator () ()
	{
		int r = 0;
		while (BFS())
			r += DFS(0, 1000000000);
		return r;
	}
};
flow < 40000, 20000000 > G;
int N, M, ToS[32769], ToT[32769], n;
void build(int p, int l, int r)
{
	if (l == r)
	{
		ToS[p] = l;
		ToT[p] = N + l;
		G.add(0, l, 1);
		G.add(N + l, G.N - 1, 1);
		return;
	}
	int m = (l + r) >> 1;
	ToS[p] = ++n;
	ToT[p] = ++n;
	build(p << 1, l, m);
	build(p << 1 | 1, m + 1, r);
	G.add(ToS[p << 1], ToS[p], 1000000000);
	G.add(ToS[p << 1 | 1], ToS[p], 1000000000);
	G.add(ToT[p], ToT[p << 1], 1000000000);
	G.add(ToT[p], ToT[p << 1 | 1], 1000000000);
}
void get(int p, int l, int r, int L, int R, int *&c, int *T)
{
	if (L <= l && r <= R)
	{
		*c++ = T[p];
		return;
	}
	int m = (l + r) >> 1;
	if (L <= m)
		get(p << 1, l, m, L, R, c, T);
	if (R > m)
		get(p << 1 | 1, m + 1, r, L, R, c, T);
}
void rect(int l, int r, int L, int R)
{
	static int qL[100], qR[100];
	int *cL = qL, *cR = qR;
	get(1, 1, N, l, r, cL, ToS);
	get(1, 1, N, L, R, cR, ToT);
	for (int *i = qL; i < cL; i++)
		for (int *j = qR; j < cR; j++)
			G.add(*i, *j, 1000000000);
}
std::vector < std::pair < int, int > > INC[10002], DEC[10002];
std::map < std::pair < int, int >, std::vector < std::pair < int, int > > > ALL;
struct SegmentTree
{
	int OR[32769], AND[32769];
	inline void down(int p)
	{
		if (!OR[p])
			OR[p + p] = OR[p + p + 1] = AND[p + p] = AND[p + p + 1] = 0;
		if (AND[p])
			OR[p + p] = OR[p + p + 1] = AND[p + p] = AND[p + p + 1] = 1;
	}
	void P(int p, int l, int r, int L, int R, int w)
	{
		if (L <= l && r <= R)
		{
			OR[p] = AND[p] = w;
			return;
		}
		int m = (l + r) >> 1;
		down(p);
		if (L <= m)
			P(p + p, l, m, L, R, w);
		if (R > m)
			P(p + p + 1, m + 1, r, L, R, w);
		OR[p] = OR[p + p] | OR[p + p + 1];
		AND[p] = AND[p + p] & AND[p + p + 1];
	}
	inline void inc(int l, int r)
	{
		P(1, 1, N, l, r, 1);
	}
	inline void dec(int l, int r)
	{
		P(1, 1, N, l, r, 0);
	}
	int ln(int p, int l, int r, int x)
	{
		if (AND[p] == 1)
			return -1;
		if (r <= x)
		{
			while (l < r)
			{
				int m = (l + r) >> 1;
				down(p);
				if (AND[p + p + 1] == 0)
					l = m + 1, p = p + p + 1;
				else
					r = m, p = p + p;
			}
			return l;
		}
		down(p);
		int m = (l + r) >> 1, RA = m < x ? ln(p + p + 1, m + 1, r, x) : -1;
		return ~RA ? RA : ln(p + p, l, m, x);
	}
	int rn(int p, int l, int r, int x)
	{
		if (AND[p] == 1)
			return -1;
		if (l >= x)
		{
			while (l < r)
			{
				int m = (l + r) >> 1;
				down(p);
				if (AND[p + p] == 0)
					r = m, p = p + p;
				else
					l = m + 1, p = p + p + 1;
			}
			return l;
		}
		down(p);
		int m = (l + r) >> 1, LA = m >= x ? rn(p + p, l, m, x) : -1;
		return ~LA ? LA : rn(p + p + 1, m + 1, r, x);
	}
	inline int lnear(int x)
	{
		int tmp = x < 1 ? -1 : ln(1, 1, N, x);
		return tmp == -1 ? 1 : tmp + 1;
	}
	inline int rnear(int x)
	{
		int tmp = x > N ? -1 : rn(1, 1, N, x);
		return tmp == -1 ? N : tmp - 1;
	}
}
seg;
void Insert(int L, int R, std::vector < std::pair < int, int > > &V)
{
	auto left = V.begin();
	int last = -1;
	while (left != V.end())
	{
		int num = left -> second;
		auto right = left + 1;
		while (right != V.end() && left -> first == right -> first)
			num += right++ -> second;
		if (num == 1)
		{
			assert(last == -1);
			last = left -> first;
		}
		else if (num == -1)
		{
			assert(last != -1);
			rect(last, left -> first - 1, L, R);
			last = -1;
		}
		else
			assert(num == 0);
		left = right;
	}
}
int main()
{
	scanf("%d", &N);
	G.clear(N << 2);
	n = N + N;
	build(1, 1, N);
	INC[1].push_back({ 1, N });
	DEC[N + 1].push_back({ 1, N });
	for (scanf("%d", &M); M--; )
	{
		int a, b, c, d;
		scanf("%d%d%d%d", &a, &b, &c, &d);
		DEC[a].push_back({ b, d });
		INC[c + 1].push_back({ b, d });
	}
	for (int i = 1; i <= N + 1; i++)
	{
		for (auto j : INC[i])
		{
			int Lext = seg.lnear(j.first - 1), Rext = seg.rnear(j.second + 1);
			if (Lext < j.first)
				ALL[{ Lext, j.first - 1 }].push_back({ i, -1 });
			if (Rext > j.second)
				ALL[{ j.second + 1, Rext }].push_back({ i, -1 });
			ALL[{ Lext, Rext }].push_back({ i, 1 });
			seg.inc(j.first, j.second);
		}
		for (auto j : DEC[i])
		{
			int Lext = seg.lnear(j.first - 1), Rext = seg.rnear(j.second + 1);
			if (Lext < j.first)
				ALL[{ Lext, j.first - 1 }].push_back({ i, 1 });
			if (Rext > j.second)
				ALL[{ j.second + 1, Rext }].push_back({ i, 1 });
			ALL[{ Lext, Rext }].push_back({ i, -1 });
			seg.dec(j.first, j.second);
		}
	}
	for (auto &i : ALL)
		Insert(i.first.first, i.first.second, i.second);
	printf("%d\n", G());
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1;
int n,m,dis[8*N + 2],q[8*N + 2],src,sink,rem,last[8*N + 2],llast[8*N + 2],seg[2][4*N],id;
struct rectangle{
	int x1,x2,y;
	rectangle(int a = 0,int b = 0,int c = 0){
		x1 = a;x2 = b;y = c;
	}
	bool operator<(const rectangle &a)const{
		if(x2 != a.x2)return x2 < a.x2;
		return x1 > a.x1;
	}
};
struct edge{
	int u,v,c,l;
}e[500*N];
void addEdge(int u,int v,int c){
	e[rem].u = u;e[rem].v = v;e[rem].c = c;e[rem].l = last[u];
	last[u] = rem++;
	e[rem].u = v;e[rem].v = u;e[rem].c = 0;e[rem].l = last[v];
	last[v] = rem++;
}
vector<int>v;
void fill1(int c,int l,int r,int s,int e){
	if(l > e || r < s)return;
	if(l >= s && r <= e){
		v.push_back(c);
		return;
	}
	fill1(2*c + 1,l,(l + r)/2,s,e);
	fill1(2*c + 2,(l + r)/2 + 1,r,s,e);
}
void want(int a,int b,int c,int d){
	v.clear();
	vector<int>temp;
	fill1(0,0,n - 1,a,b);
	temp = v;
	v.clear();
	fill1(0,0,n - 1,c,d);
	for(auto i : temp)
		for(auto j : v)
			addEdge(seg[0][i],seg[1][j],1e9);
}
void build(int c,int l,int r,bool is){
	if(l == r){
		v.push_back(c);
		return;
	}
	if(is){
		addEdge(seg[1][c],seg[1][2*c + 1],1e9);
		addEdge(seg[1][c],seg[1][2*c + 2],1e9);
	}
	else{
		addEdge(seg[0][2*c + 1],seg[0][c],1e9);
		addEdge(seg[0][2*c + 2],seg[0][c],1e9);
	}
	build(2*c + 1,l,(l + r)/2,is);
	build(2*c + 2,(l + r)/2 + 1,r,is);
}
set<rectangle>st[N];
bool bfs(){
	memset(dis,-1,sizeof dis);
	dis[src] = 0;
	q[0] = src;
	int l = 0,r = 0;
	while(l <= r){
		int x = q[l++];
		for(int cur = last[x];cur != -1;cur = e[cur].l)
			if(e[cur].c && dis[e[cur].v] == -1){
				dis[e[cur].v] = dis[x] + 1;
				q[++r] = e[cur].v;
			}
	}
	return dis[sink] != -1;
}
bool dfs(int u){
	if(u == sink)return 1;
	for(int &cur = llast[u];cur != -1;cur = e[cur].l)
		if(e[cur].c && dis[e[cur].v] == dis[u] + 1 && dfs(e[cur].v)){
			e[cur].c--;
			e[cur^1].c++;
			return 1;
		}
	return 0;
}
int MaxFlow(){
	int flow = 0;
	while(bfs()){
		for(int i = 0;i < id;i++)llast[i] = last[i];
		while(dfs(src))flow++;
	}
	return flow;
}
void number(int c,int l,int r,bool at){
	seg[at][c] = id++;
	if(l == r)return;
	number(2*c + 1,l,(l + r)/2,at);
	number(2*c + 2,(l + r)/2 + 1,r,at);
}
int main(){
// 	freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&m);
	number(0,0,n - 1,0);
	number(0,0,n - 1,1);
	src = id++;sink = id++;
	memset(last,-1,sizeof last);
	for(int i = 0,x1,x2,y1,y2;i < m;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		st[y1 - 1].insert(rectangle(x1 - 1,x2 - 1,y2 - 1));
	}
	st[n].insert(rectangle(0,n - 1,n));
	set<rectangle>have;
	have.insert(rectangle(0,n - 1,0));
	for(int i = 0;i <= n;i++){
		for(auto j : st[i]){
			vector<rectangle>temp;
			swap(j.x1,j.x2);
			auto at = have.lower_bound(j);
			swap(j.x1,j.x2);
			while(at != have.end() && at->x1 <= j.x2){
				rectangle now = *at;
				at = have.erase(at);
				if(now.x1 < j.x1){
					temp.push_back(rectangle(now.x1,j.x1 - 1,now.y));
					now.x1 = j.x1;
				}
				if(now.x2 > j.x2){
					temp.push_back(rectangle(j.x2 + 1,now.x2,now.y));
					now.x2 = j.x2;
				}
				if(now.y < i)want(now.x1,now.x2,now.y,i - 1);
			}
			for(auto j : temp)have.insert(j);
			j.y++;
			have.insert(j);
		}
	}
	v.clear();
	build(0,0,n - 1,0);
	for(auto i : v)addEdge(src,seg[0][i],1);
	v.clear();
	build(0,0,n - 1,1);
	for(auto i : v)addEdge(seg[1][i],sink,1);
	printf("%d\n",MaxFlow());
}

#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <list>
#include <cassert>
#include <ctime>
#include <climits>
#include <random>
using namespace std;

#define PB push_back
#define MP make_pair
#define SZ(v) ((int)(v).size())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define REP(i,n) FOR(i,0,n)
#define FORE(i,a,b) for(int i=(a);i<=(b);++i)
#define REPE(i,n) FORE(i,0,n)
#define FORSZ(i,a,v) FOR(i,a,SZ(v))
#define REPSZ(i,v) REP(i,SZ(v))
typedef long long ll;
typedef unsigned long long ull;
ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b); }

const int MAXN=10000;
const int MAXBLOCKS=10000;
const int MAXRECT=4*MAXBLOCKS;
const int MAXGN=2+4*MAXN+MAXRECT;
const int MAXGM=6*MAXN+2*2*14*MAXRECT;
typedef struct R { int lx,ly,hx,hy; } R;

int n,h,w;
R b[MAXBLOCKS]; int nb;
bool blocked[MAXN][MAXN];
R r[MAXRECT]; int nr;

typedef struct MF {
	int n,m;
	int ghead[MAXGN],gnxt[2*MAXGM],gto[2*MAXGM],gcap[2*MAXGM];
	void init(int _n) { n=_n,m=0; REP(i,n) ghead[i]=-1; }
	void addedge(int a,int b,int c) { gnxt[2*m+0]=ghead[a],ghead[a]=2*m+0,gto[2*m+0]=b,gcap[2*m+0]=c; gnxt[2*m+1]=ghead[b],ghead[b]=2*m+1,gto[2*m+1]=a,gcap[2*m+1]=0; ++m; /*printf("%d->%d\n",a,b);*/ }
	int d[MAXGN];
	int q[MAXGN],qhead,qtail;
	int cur[MAXGN];
	int dinic(int s,int t) {
		int ret=0; int niter=0;
		while(true) {
			REP(i,n) d[i]=INT_MAX; qhead=qtail=0; d[s]=0,q[qhead++]=s;
			while(qtail<qhead&&d[t]==INT_MAX) { int at=q[qtail++];for(int x=ghead[at];x!=-1;x=gnxt[x]) { int to=gto[x]; if(gcap[x]==0||d[to]!=INT_MAX) continue; d[to]=d[at]+1; q[qhead++]=to; } }
			if(d[t]==INT_MAX) { /*printf("niter=%d\n",niter);*/ return ret; }
			REP(i,n) cur[i]=ghead[i];
			ret+=dfs(s,t,INT_MAX); ++niter;
		}
	}
	int dfs(int at,int t,int rem) {
		int ret=0; if(at==t) return rem;
		if(d[at]>=d[t]) return 0;
		for(;cur[at]!=-1;cur[at]=gnxt[cur[at]]) {
			int to=gto[cur[at]],cap=gcap[cur[at]]; if(d[to]!=d[at]+1||cap==0) continue;
			int now=dfs(to,t,min(cap,rem));
			rem-=now; ret+=now; gcap[cur[at]]-=now; gcap[cur[at]^1]+=now; if(rem==0) return ret;
		}
		return ret;
	}
	void print() { REP(i,m) if(gcap[2*i+0]==0) printf("used %d->%d\n",gto[2*i+1],gto[2*i+0]); }
} MF;
MF g;

int solve() {
	memset(blocked,false,sizeof(blocked)); h=w=n;
	REP(i,nb) FORE(x,b[i].lx,b[i].hx) FORE(y,b[i].ly,b[i].hy) blocked[x][y]=true;

	nr=0;
	REP(lx,h) REP(ly,w) if(!blocked[lx][ly]) {
		int hy=ly; while(hy+1<w&&!blocked[lx][hy+1]) ++hy;
		int hx=lx; while(hx+1<h) { bool ok=true; FORE(y,ly,hy) if(blocked[hx+1][y]) { ok=false; break; } if(!ok) break; else ++hx; }
		r[nr].lx=lx,r[nr].ly=ly,r[nr].hx=hx,r[nr].hy=hy,++nr;
		FORE(x,lx,hx) FORE(y,ly,hy) blocked[x][y]=true;
	}
	//printf("nr=%d\n",nr);
	//REP(i,nr) printf("(%d..%d)/(%d..%d)\n",r[i].lx,r[i].hx,r[i].ly,r[i].hy);

	g.init(2+4*n+nr);
	//printf("g.n=%d\n",g.n);
	REP(i,n) g.addedge(0,2+n+i,1);
	FOR(i,1,n) g.addedge(2+2*i,2+i,n),g.addedge(2+2*i+1,2+i,n);
	REP(i,n) g.addedge(2+3*n+i,1,1);
	FOR(i,1,n) g.addedge(2+2*n+i,2+2*n+2*i,n),g.addedge(2+2*n+i,2+2*n+2*i+1,n);
	//printf("g.m=%d\n",g.m);
	REP(i,nr) {
		{
			int lidx=n+r[i].lx,ridx=n+r[i].hx+1;
			for(;lidx<ridx;lidx>>=1,ridx>>=1) {
				if(lidx&1) g.addedge(2+lidx,2+4*n+i,n),++lidx;
				if(ridx&1) --ridx,g.addedge(2+ridx,2+4*n+i,n);
			}
		}
		{
			int lidx=n+r[i].ly,ridx=n+r[i].hy+1;
			for(;lidx<ridx;lidx>>=1,ridx>>=1) {
				if(lidx&1) g.addedge(2+4*n+i,2+2*n+lidx,n),++lidx;
				if(ridx&1) --ridx,g.addedge(2+4*n+i,2+2*n+ridx,n);
			}
		}
	}
	//printf("g.m=%d\n",g.m);

	int ret=g.dinic(0,1);
	return ret;
}

void run() {
	scanf("%d",&n);
	scanf("%d",&nb); REP(i,nb) { int lx,ly,hx,hy; scanf("%d%d%d%d",&b[i].lx,&b[i].ly,&b[i].hx,&b[i].hy); --b[i].lx,--b[i].ly,--b[i].hx,--b[i].hy;  }
	printf("%d\n",solve());
}

void stress() {
	n=MAXN; nb=MAXBLOCKS-1; REP(i,nb) { b[i].lx=i,b[i].hx=i,b[i].ly=(rand()*1000+rand())%n,b[i].hy=n-1; }
	//printf("%d\n%d\n",n,nb); REP(i,nb) printf("%d %d %d %d\n",b[i].lx+1,b[i].ly+1,b[i].hx+1,b[i].hy+1); return;
	clock_t begin = clock();
	printf("%d\n",solve());
	clock_t end = clock(); printf("%.9lf\n",(double)(end-begin)/CLOCKS_PER_SEC);
}

int main() {
	//stress();
	run();
	return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define INF 10000
int n, q, m;
unordered_map<int, int> *resi;

// Max flow (DINIC)
struct FLOW {
    typedef unordered_map<int, int>::const_iterator mci;
    int n, *dist;
    mci *rix;
    FLOW(int n) : n(n) {
        dist = (int *)malloc((n + 2) * sizeof(int));
        rix = (mci *)malloc((n + 2) * sizeof(mci));
        for (int i = 0; i < n + 2; i++)
            for (const auto &ri : resi[i])
                if (ri.second != 0)
                    resi[ri.first][i] = 0;
    }
    ~FLOW() {
        free(dist);
        free(rix);
    }

    // DINIC Algorithm (O(n^2m). for capacity=1, O(m sqrt(n)))
    int DINIC() {
        int rs = 0;
        for (; DBFS(); rs += DDFS(n, INF))
            ;
        return rs;
    }

    // DINIC DFS
    int DDFS(int x, int flow) {
        if (x == n + 1)
            return flow;
        int used = 0;
        for (mci ri = rix[x]; ri != resi[x].end(); ri++) {
            int i = (rix[x] = ri)->first, fx;
            if (ri->second != 0 && dist[i] == dist[x] + 1) {
                fx = DDFS(i, min(flow - used, ri->second));
                resi[x][i] -= fx;
                resi[i][x] += fx;
                used += fx;
                if (used == flow)
                    return used;
            }
        }
        if (!used)
            dist[x] = -1;
        return used;
    }

    // DINIC BFS
    bool DBFS() {
        int nn = n + 2;
        for (int i = 0; i < nn; i++)
            rix[i] = resi[i].begin();
        memset(dist, -1, nn * sizeof(int));
        dist[n] = 0;
        int q[nn], h = 0, r = 1, x;
        q[0] = n;
        while (h < r) {
            if ((x = q[h++]) == n + 1)
                return true;
            for (const auto &ri : resi[x])
                if (ri.second != 0 && dist[ri.first] == -1)
                    dist[ri.first] = dist[x] + 1, q[r++] = ri.first;
        }
        return dist[n + 1] != -1;
    }
};

void update_rect(const set<array<int, 2>> &lazy,
                 const vector<array<int, 2>> &rts, vector<array<int, 2>> &rr,
                 vector<array<int, 2>> &rz) {
    auto ki = lazy.begin();
    auto kj = rts.begin();
    int z1 = (*ki)[0], z2 = (*ki)[1], x1 = (*kj)[0], x2 = (*kj)[1];
    while (ki != lazy.end() || kj != rts.end()) {
        while (ki != lazy.end()) {
            if (z1 == -1)
                z1 = (*ki)[0], z2 = (*ki)[1];
            auto kix = ++ki;
            ki--;
            if (kix != lazy.end() && (*kix)[0] == z2) {
                z2 = (*kix)[1];
                ki++;
            } else
                break;
        }
        if (ki == lazy.end()) {
            if (x1 != -1) {
                rr.push_back(array<int, 2>{x1, x2});
                kj++;
            }
            rr.reserve(rts.end() - kj);
            rr.insert(rr.end(), kj, rts.end());
            kj = rts.end();
            break;
        } else if (kj == rts.end())
            break;
        if (z1 == -1)
            z1 = (*ki)[0], z2 = (*ki)[1];
        if (x1 == -1)
            x1 = (*kj)[0], x2 = (*kj)[1];
        if (z2 <= x1)
            ki++, z1 = -1;
        else if (z2 < x2 && z2 > x1 && z1 <= x1) {
            rz.push_back(array<int, 2>{x1, z2});
            x1 = z2;
            ki++, z1 = -1;
        } else if (z2 < x2 && z2 > x1 && z1 > x1) {
            rr.push_back(array<int, 2>{x1, z1});
            rz.push_back(array<int, 2>{z1, z2});
            x1 = z2;
            ki++, z1 = -1;
        } else if (z1 >= x2) {
            rr.push_back(array<int, 2>{x1, x2});
            kj++, x1 = -1;
        } else if (z2 >= x2 && z1 > x1 && z1 < x2) {
            rr.push_back(array<int, 2>{x1, z1});
            rz.push_back(array<int, 2>{z1, x2});
            kj++, x1 = -1;
        } else if (z2 >= x2 && z1 <= x1) {
            rz.push_back(array<int, 2>{x1, x2});
            kj++, x1 = -1;
        }
    }
}

// persistent segment tree
struct SEG {
    int size, m;
    bool *vis;
    set<array<int, 2>> *lazy, *xlazy;
    SEG(int n) {
        for (this->m = 1; this->m < n; this->m <<= 1)
            ;
        this->size = n;
        vis = new bool[2 * this->m];
        memset(this->vis, false, 2 * this->m * sizeof(bool));
        lazy = new set<array<int, 2>>[ 2 * this->m ];
        xlazy = new set<array<int, 2>>[ 2 * this->m ];
    }
    ~SEG() { delete[] vis, lazy, xlazy; }
    void create_graph(int base, bool dir, int start = -1, int end = -1,
                      int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        if (start + 1 == end) {
            if (dir)
                resi[base + i][m * 4 + 1] = 1;
            else
                resi[m * 4][base + i] = 1;
        } else {
            int mid = (start + end) / 2;
            this->create_graph(base, dir, start, mid, i * 2);
            this->create_graph(base, dir, mid, end, i * 2 + 1);
            if (dir) {
                resi[base + i][base + i * 2] = INF;
                resi[base + i][base + i * 2 + 1] = INF;
            } else {
                resi[base + i * 2][base + i] = INF;
                resi[base + i * 2 + 1][base + i] = INF;
            }
        }
    }
    void create_edge(int node, int l, int r, int start = -1, int end = -1,
                     int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        if (r <= start || l >= end)
            ;
        else if (l <= start && r >= end)
            resi[node][m * 2 + i] = INF;
        else {
            int mid = (start + end) / 2;
            this->create_edge(node, l, r, start, mid, i * 2);
            this->create_edge(node, l, r, mid, end, i * 2 + 1);
        }
    }
    void update(int l, int r, const array<int, 2> &val, int start = -1,
                int end = -1, int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        if (r <= start || l >= end)
            ;
        else if (l <= start && r >= end) {
            this->vis[i] = true;
            this->lazy[i].insert(val);
        } else {
            this->vis[i] = true;
            this->xlazy[i].insert(val);
            int mid = (start + end) / 2;
            this->update(l, r, val, start, mid, i * 2);
            this->update(l, r, val, mid, end, i * 2 + 1);
        }
    }
    void query(const vector<array<int, 2>> &rts, int start = -1, int end = -1,
               int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        const vector<array<int, 2>> *prt = &rts;
        vector<array<int, 2>> rr, rz, rs;
        if (this->lazy[i].size() != 0) {
            update_rect(this->lazy[i], *prt, rs, rz);
            prt = &rs;
            rz.clear();
        }
        if (this->xlazy[i].size() != 0) {
            update_rect(this->xlazy[i], *prt, rr, rz);
            prt = &rr;
        }
        if (start + 1 < end && (this->vis[i * 2] || this->vis[i * 2 + 1])) {
            int mid = (start + end) / 2;
            this->query(rz, start, mid, i * 2);
            this->query(rz, mid, end, i * 2 + 1);
        }
        for (auto &rt : *prt)
            if (rt[0] != -1)
                this->create_edge(i, rt[0], rt[1]);
    }
};

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> q;
    int x1, x2, y1, y2;
    SEG seg(n);
    m = seg.m;
    resi = new unordered_map<int, int>[ m * 4 + 2 ];
    seg.create_graph(0, false);
    seg.create_graph(m * 2, true);
    for (int i = 0; i < q; i++) {
        cin >> x1 >> y1 >> x2 >> y2;
        array<int, 2> val = {y1 - 1, y2};
        seg.update(x1 - 1, x2, val);
    }
    vector<array<int, 2>> rts = {{0, n}};
    seg.query(rts);
    FLOW flow(m * 4);
    cout << flow.DINIC() << endl;
    delete[] resi;
    return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define INF 10000
int n, q, m;
unordered_map<int, int> *resi;

// Max flow (DINIC)
struct FLOW {
    typedef unordered_map<int, int>::const_iterator mci;
    int n, *dist;
    mci *rix;
    FLOW(int n) : n(n) {
        dist = (int *)malloc((n + 2) * sizeof(int));
        rix = (mci *)malloc((n + 2) * sizeof(mci));
        for (int i = 0; i < n + 2; i++)
            for (const auto &ri : resi[i])
                if (ri.second != 0)
                    resi[ri.first][i] = 0;
    }
    ~FLOW() {
        free(dist);
        free(rix);
    }

    // DINIC Algorithm (O(n^2m). for capacity=1, O(m sqrt(n)))
    int DINIC() {
        int rs = 0;
        for (; DBFS(); rs += DDFS(n, INF))
            ;
        return rs;
    }

    // DINIC DFS
    int DDFS(int x, int flow) {
        if (x == n + 1)
            return flow;
        int used = 0;
        for (mci ri = rix[x]; ri != resi[x].end(); ri++) {
            int i = (rix[x] = ri)->first, fx;
            if (ri->second != 0 && dist[i] == dist[x] + 1) {
                fx = DDFS(i, min(flow - used, ri->second));
                resi[x][i] -= fx;
                resi[i][x] += fx;
                used += fx;
                if (used == flow)
                    return used;
            }
        }
        if (!used)
            dist[x] = -1;
        return used;
    }

    // DINIC BFS
    bool DBFS() {
        int nn = n + 2;
        for (int i = 0; i < nn; i++)
            rix[i] = resi[i].begin();
        memset(dist, -1, nn * sizeof(int));
        dist[n] = 0;
        int q[nn], h = 0, r = 1, x;
        q[0] = n;
        while (h < r) {
            if ((x = q[h++]) == n + 1)
                return true;
            for (const auto &ri : resi[x])
                if (ri.second != 0 && dist[ri.first] == -1)
                    dist[ri.first] = dist[x] + 1, q[r++] = ri.first;
        }
        return dist[n + 1] != -1;
    }
};

void update_rect(const set<array<int, 2>> &lazy,
                 const vector<array<int, 2>> &rts, vector<array<int, 2>> &rr,
                 vector<array<int, 2>> &rz) {
    auto ki = lazy.begin();
    auto kj = rts.begin();
    int z1 = (*ki)[0], z2 = (*ki)[1], x1 = (*kj)[0], x2 = (*kj)[1];
    while (ki != lazy.end() || kj != rts.end()) {
        // while (ki != lazy.end()) {
        //     auto kix = ++ki;
        //     ki--;
        //     if (kix != lazy.end() && (*kix)[0] == z2) {
        //         z2 = (*kix)[1];
        //         ki++;
        //     } else
        //         break;
        // }
        if (ki == lazy.end()) {
            if (x1 != -1) {
                rr.push_back(array<int, 2>{x1, x2});
                kj++;
            }
            rr.reserve(rts.end() - kj);
            rr.insert(rr.end(), kj, rts.end());
            kj = rts.end();
            break;
        } else if (kj == rts.end())
            break;
        if (z1 == -1)
            z1 = (*ki)[0], z2 = (*ki)[1];
        if (x1 == -1)
            x1 = (*kj)[0], x2 = (*kj)[1];
        if (z2 <= x1)
            ki++, z1 = -1;
        else if (z2 < x2 && z2 > x1 && z1 <= x1) {
            rz.push_back(array<int, 2>{x1, z2});
            x1 = z2;
            ki++, z1 = -1;
        } else if (z2 < x2 && z2 > x1 && z1 > x1) {
            rr.push_back(array<int, 2>{x1, z1});
            rz.push_back(array<int, 2>{z1, z2});
            x1 = z2;
            ki++, z1 = -1;
        } else if (z1 >= x2) {
            rr.push_back(array<int, 2>{x1, x2});
            kj++, x1 = -1;
        } else if (z2 >= x2 && z1 > x1 && z1 < x2) {
            rr.push_back(array<int, 2>{x1, z1});
            rz.push_back(array<int, 2>{z1, x2});
            kj++, x1 = -1;
        } else if (z2 >= x2 && z1 <= x1) {
            rz.push_back(array<int, 2>{x1, x2});
            kj++, x1 = -1;
        }
    }
}

// persistent segment tree
struct SEG {
    int size, m;
    bool *vis;
    set<array<int, 2>> *lazy, *xlazy;
    SEG(int n) {
        for (this->m = 1; this->m < n; this->m <<= 1)
            ;
        this->size = n;
        vis = new bool[2 * this->m];
        memset(this->vis, false, 2 * this->m * sizeof(bool));
        lazy = new set<array<int, 2>>[ 2 * this->m ];
        xlazy = new set<array<int, 2>>[ 2 * this->m ];
    }
    ~SEG() { delete[] vis, lazy, xlazy; }
    void create_graph(int base, bool dir, int start = -1, int end = -1,
                      int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        if (start + 1 == end) {
            if (dir)
                resi[base + i][m * 4 + 1] = 1;
            else
                resi[m * 4][base + i] = 1;
        } else {
            int mid = (start + end) / 2;
            this->create_graph(base, dir, start, mid, i * 2);
            this->create_graph(base, dir, mid, end, i * 2 + 1);
            if (dir) {
                resi[base + i][base + i * 2] = INF;
                resi[base + i][base + i * 2 + 1] = INF;
            } else {
                resi[base + i * 2][base + i] = INF;
                resi[base + i * 2 + 1][base + i] = INF;
            }
        }
    }
    void create_edge(int node, int l, int r, int start = -1, int end = -1,
                     int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        if (r <= start || l >= end)
            ;
        else if (l <= start && r >= end)
            resi[node][m * 2 + i] = INF;
        else {
            int mid = (start + end) / 2;
            this->create_edge(node, l, r, start, mid, i * 2);
            this->create_edge(node, l, r, mid, end, i * 2 + 1);
        }
    }
    void update(int l, int r, const array<int, 2> &val, int start = -1,
                int end = -1, int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        if (r <= start || l >= end)
            ;
        else if (l <= start && r >= end) {
            this->vis[i] = true;
            this->lazy[i].insert(val);
        } else {
            this->vis[i] = true;
            this->xlazy[i].insert(val);
            int mid = (start + end) / 2;
            this->update(l, r, val, start, mid, i * 2);
            this->update(l, r, val, mid, end, i * 2 + 1);
        }
    }
    void query(const vector<array<int, 2>> &rts, int start = -1, int end = -1,
               int i = 1) {
        if (i == 1)
            start = 0, end = this->size;
        const vector<array<int, 2>> *prt = &rts;
        vector<array<int, 2>> rr, rz, rs;
        if (this->lazy[i].size() != 0) {
            update_rect(this->lazy[i], *prt, rs, rz);
            prt = &rs;
            rz.clear();
        }
        if (this->xlazy[i].size() != 0) {
            update_rect(this->xlazy[i], *prt, rr, rz);
            prt = &rr;
        }
        if (start + 1 < end && (this->vis[i * 2] || this->vis[i * 2 + 1])) {
            int mid = (start + end) / 2;
            this->query(rz, start, mid, i * 2);
            this->query(rz, mid, end, i * 2 + 1);
        }
        for (auto &rt : *prt)
            if (rt[0] != -1)
                this->create_edge(i, rt[0], rt[1]);
    }
};

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> q;
    int x1, x2, y1, y2;
    SEG seg(n);
    m = seg.m;
    resi = new unordered_map<int, int>[ m * 4 + 2 ];
    seg.create_graph(0, false);
    seg.create_graph(m * 2, true);
    for (int i = 0; i < q; i++) {
        cin >> x1 >> y1 >> x2 >> y2;
        array<int, 2> val = {y1 - 1, y2};
        seg.update(x1 - 1, x2, val);
    }
    vector<array<int, 2>> rts = {{0, n}};
    seg.query(rts);
    FLOW flow(m * 4);
    cout << flow.DINIC() << endl;
    delete[] resi;
    return 0;
}

//18:48
//19:38
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+50,inf=0x3f3f3f3f;
inline int rd(register int x=0,register char ch=getchar(),register int f=0){
	for(;!isdigit(ch);ch=getchar()) f=ch=='-';
	for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-48;
	return f?-x:x;
}
int n,m,ptr,B,S,T,rt0,rt1;
int head[N*4*2],nxt[N*4*2*20],to[N*4*2*20],flo[N*4*2*20],ls[N*4],rs[N*4],dep[N*8],cur[N*8],lst[N];
struct node{
	int i,op,l,r;
	friend bool operator < (node a,node b){
		return a.i!=b.i?a.i<b.i:a.op>b.op;
	}
}q[N*2];

void lnk(int x,int y,int flow){
	to[++B]=y,nxt[B]=head[x],head[x]=B,flo[B]=flow;
	to[++B]=x,nxt[B]=head[y],head[y]=B,flo[B]=0;
}
void build(int &x,int l,int r,int d){
	x=++ptr;if(l==r) return (d==0?lnk(S,x,1):lnk(x,T,1)),void();
	int mid=(l+r)>>1;
	build(ls[x],l,mid,d),build(rs[x],mid+1,r,d);
	if(d==0) lnk(ls[x],x,inf),lnk(rs[x],x,inf);
	else lnk(x,ls[x],inf),lnk(x,rs[x],inf);
}
void add(int x,int l,int r,int L,int R,int d){
	if(L>R) return;
	if(L<=l&&r<=R) return (d==0?lnk(x,ptr,inf):lnk(ptr,x,inf)),void();
	int mid=(l+r)>>1;
	if(L<=mid) add(ls[x],l,mid,L,R,d);
	if(R>mid) add(rs[x],mid+1,r,L,R,d);
}
void update(int op,int x,int l,int r){
	if(op==1){for(int i=l;i<=r;++i) lst[i]=x;return;}
	//printf("update:%d %d %d\n",x,l,r);
	for(int i=l;i<=r;){
		int j=i;
		while(j<=r&&lst[i]==lst[j]) ++j;
		if(lst[i]==-1) {i=j;continue;}
		//printf("[%d,%d]->[%d,%d]\n",lst[i]+1,x,i,j-1);
		++ptr,add(rt0,1,n,lst[i]+1,x,0),add(rt1,1,n,i,j-1,1);
		i=j;
	}
	for(int j=l;j<=r;++j) lst[j]=-1;
}
bool bfs(){
	queue<int> q;
	for(int i=1;i<=ptr;++i) dep[i]=0;
	q.push(S); dep[S]=1; cur[S]=head[S];
	while(!q.empty()){
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=nxt[i])if(flo[i]&&!dep[to[i]]){
			dep[to[i]]=dep[u]+1;
			cur[to[i]]=head[to[i]];
			if(to[i]==T) return 1;
			q.push(to[i]);
		}
	}
	return 0;
}
int dfs(int u,int cup){
	if(u==T) return cup;
	int rem=cup;
	for(int &i=cur[u];i;i=nxt[i]) if(flo[i]&&dep[to[i]]==dep[u]+1){
		int v=dfs(to[i],min(flo[i],rem));
		if(!v) dep[to[i]]=0;
		rem-=v,flo[i]-=v,flo[i^1]+=v;
		if(!rem) break;
	}
	return cup-rem;
}
int main(){
	n=rd(); m=rd();B=1;S=1;T=2;ptr=2;
	build(rt0,1,n,0),build(rt1,1,n,1);
	for(int i=1;i<=m;++i){
		int sx=rd(),sy=rd(),tx=rd(),ty=rd();
		q[i*2-1]=(node){sx-1,0,sy,ty};
		q[i*2]=(node){tx,1,sy,ty};
	}
	sort(q+1,q+m*2+1);
	for(int i=1;i<=m*2;++i) update(q[i].op,q[i].i,q[i].l,q[i].r);
	update(0,n,1,n);
	int maxflow=0;
	while(bfs()) maxflow+=dfs(S,inf);
	printf("%d\n",maxflow);
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10050;
const int D = 600050;
const int M = 3000050;
const int INF = 20000000;

int s, t, nds;
namespace Maxflow{
	int dis[D], q[M], f, r, tot = 1;
	int head[D], to[M], nex[M], fl[M], now[D];
	void addedge(int u, int v, int f){
		tot++; to[tot]=v, nex[tot]=head[u], head[u]=tot, fl[tot]=f;
		tot++; to[tot]=u, nex[tot]=head[v], head[v]=tot, fl[tot]=0;
	}
	bool spfa(){
		int i, j;
		q[f = r = 1] = s;
		for(i = 2; i <= nds; i ++)
			dis[i] = INF;
		while(f <= r){
			j = q[f ++];
			for(i = head[j]; i; i = nex[i]){
				if(fl[i] && dis[to[i]] > dis[j] + 1){
					dis[to[i]] = dis[j] + 1;
					q[++r] = to[i];
				}
			}
		}
		return dis[t] != INF;
	}
	int dinic(int x, int flow){
		if(x == t)
			return flow;
		for(int i = now[x]; i; now[x] = i = nex[i]){
			if(fl[i] && dis[x] + 1 == dis[to[i]]){
				int tmp = dinic(to[i], min(fl[i], flow));
				if(tmp != 0){
					fl[i] -= tmp;
					fl[i^1] += tmp;
					return tmp;
				}
			}
		}
		return 0;
	}
	int maxflow(){
		int i, j, ans = 0;
		while(spfa()){
			for(i = 1; i <= nds; i ++)
				now[i] = head[i];
			int tmp = 0;
			while(tmp = dinic(s, INF))
				ans += tmp;
		}
		return ans;
	}
}
using Maxflow :: addedge;

//s, t, col, row, tre
int lc[M], rc[M], root[2 * N], typ;
void build(int &p, int l, int r){
	p = ++nds;
	if(l == r){
		addedge(p, 2 + l, INF);
		return;
	}
	int md = l + r >>1;
	build(lc[p], l, md);
	build(rc[p], md + 1, r);
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}
void add(int &p, int las, int one, int l, int r, int x, int y){
//typ == 1:break
//typ == -1:recover
	p = ++ nds;
	if(l == x&&r == y){
		if(typ == -1){
			if(x == y)
				addedge(p, 2 + x, INF);
			else{
				lc[p] = lc[one];
				rc[p] = rc[one];
				addedge(p, lc[p], INF);
				addedge(p, rc[p], INF);
			}
		}
		return;
	}
	lc[p] = lc[las];
	rc[p] = rc[las];
	int md = l + r >>1;
	if(y <= md)
		add(lc[p], lc[las], lc[one], l, md, x, y);
	else if(x > md)
		add(rc[p], rc[las], rc[one], md + 1, r, x, y);
	else{
		add(lc[p], lc[las], lc[one], l, md, x, md);
		add(rc[p], rc[las], rc[one], md + 1, r, md + 1, y);
	}
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}

namespace io{
	const int L = (1 << 19) + 1;
	char ibuf[L], *iS, *iT, c; int f;
	inline char gc(){
		if(iS == iT){
			iT = (iS = ibuf) + fread(ibuf, 1, L, stdin);
			return iS == iT ? EOF : *iS ++;
		}
		return *iS ++;
	}
	template <class I> void read(I &x){
		for(f = 1, c = gc(); c < '0'||c > '9'; c = gc())
			if(c == '-')
				f = -1;
		for(x = 0; c <= '9'&&c >= '0'; c = gc())
			x = x * 10 + (c&15);
		x *= f;
	}
};
using io :: read;
struct line{
	int dw, up, typ, x;
	line(int xx = 0, int y1 = 0, int y2 = 0, int tp = 0){
		x = xx;
		dw = y1;
		up = y2;
		typ = tp;
	}
	inline bool operator < (const line &b)const{
		if(x == b.x)
			return typ < b.typ;
		return x < b.x;
	}
}a[2 * N];
int n, m;

int main(){

	int i, j, k, p, q;
	s = 1, read(n), read(m);
	t = 2, nds = 2 + n;
	build(root[0], 1, n);
	for(i = 1; i <= m; i ++){
		read(j), read(k), read(p), read(q);
		a[2 * i - 1] = line(k, j, p, 1);
		a[2 * i] = line(q + 1, j, p, -1);
	}
	sort(a + 1, a + 2*m + 1);
	for(i = j = 1; i <= n; i ++){
		while(j <= 2 * m&&a[j].x == i)
			typ = a[j].typ, add(root[j], root[j - 1], root[0], 1, n, a[j].dw, a[j].up), j ++;
		addedge(2 + i, t, 1), addedge(s, root[j - 1], 1);
	}
	printf("%d\n", Maxflow :: maxflow());

	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10050;
const int D = 600050;
const int M = 8000050;
const int INF = 20000000;

int s, t, nds;
namespace Maxflow{
	int dis[D], q[M * 2], f, r, tot = 1;
	int head[D], to[M], nex[M], fl[M], now[D];
	void addedge(int u, int v, int f){
		tot++; to[tot]=v, nex[tot]=head[u], head[u]=tot, fl[tot]=f;
		tot++; to[tot]=u, nex[tot]=head[v], head[v]=tot, fl[tot]=0;
	}
	bool spfa(){
		int i, j;
		q[f = r = 1] = s;
		for(i = 2; i <= nds; i ++)
			dis[i] = INF;
		while(f <= r){
			j = q[f ++];
			for(i = head[j]; i; i = nex[i]){
				if(fl[i] && dis[to[i]] > dis[j] + 1){
					dis[to[i]] = dis[j] + 1;
					q[++r] = to[i];
				}
			}
		}
		return dis[t] != INF;
	}
	int dinic(int x, int flow){
		if(x == t)
			return flow;
		for(int i = now[x]; i; now[x] = i = nex[i]){
			if(fl[i] && dis[x] + 1 == dis[to[i]]){
				int tmp = dinic(to[i], min(fl[i], flow));
				if(tmp != 0){
					fl[i] -= tmp;
					fl[i^1] += tmp;
					return tmp;
				}
			}
		}
		return 0;
	}
	int maxflow(){
		int i, j, ans = 0;
		while(spfa()){
			for(i = 1; i <= nds; i ++)
				now[i] = head[i];
			int tmp = 0;
			while(tmp = dinic(s, INF))
				ans += tmp;
		}
		return ans;
	}
}
using Maxflow :: addedge;

//s, t, col, row, tre
int lc[M], rc[M], root[2 * N], typ;
void build(int &p, int l, int r){
	p = ++nds;
	if(l == r){
		addedge(p, 2 + l, INF);
		return;
	}
	int md = l + r >>1;
	build(lc[p], l, md);
	build(rc[p], md + 1, r);
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}
void add(int &p, int las, int one, int l, int r, int x, int y){
//typ == 1:break
//typ == -1:recover
	p = ++ nds;
	if(l == x&&r == y){
		if(typ == -1){
			if(x == y)
				addedge(p, 2 + x, INF);
			else{
				lc[p] = lc[one];
				rc[p] = rc[one];
				addedge(p, lc[p], INF);
				addedge(p, rc[p], INF);
			}
		}
		return;
	}
	lc[p] = lc[las];
	rc[p] = rc[las];
	int md = l + r >>1;
	if(y <= md)
		add(lc[p], lc[las], lc[one], l, md, x, y);
	else if(x > md)
		add(rc[p], rc[las], rc[one], md + 1, r, x, y);
	else{
		add(lc[p], lc[las], lc[one], l, md, x, md);
		add(rc[p], rc[las], rc[one], md + 1, r, md + 1, y);
	}
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}

namespace io{
	const int L = (1 << 19) + 1;
	char ibuf[L], *iS, *iT, c; int f;
	inline char gc(){
		if(iS == iT){
			iT = (iS = ibuf) + fread(ibuf, 1, L, stdin);
			return iS == iT ? EOF : *iS ++;
		}
		return *iS ++;
	}
	template <class I> void read(I &x){
		for(f = 1, c = gc(); c < '0'||c > '9'; c = gc())
			if(c == '-')
				f = -1;
		for(x = 0; c <= '9'&&c >= '0'; c = gc())
			x = x * 10 + (c&15);
		x *= f;
	}
};
using io :: read;
struct line{
	int dw, up, typ, x;
	line(int xx = 0, int y1 = 0, int y2 = 0, int tp = 0){
		x = xx;
		dw = y1;
		up = y2;
		typ = tp;
	}
	inline bool operator < (const line &b)const{
		if(x == b.x)
			return typ < b.typ;
		return x < b.x;
	}
}a[2 * N];
int n, m;

int main(){

	int i, j, k, p, q;
	s = 1, read(n), read(m);
	t = 2, nds = 2 + n;
	build(root[0], 1, n);
	for(i = 1; i <= m; i ++){
		read(j), read(k), read(p), read(q);
		a[2 * i - 1] = line(k, j, p, 1);
		a[2 * i] = line(q + 1, j, p, -1);
	}
	sort(a + 1, a + 2*m + 1);
	for(i = j = 1; i <= n; i ++){
		while(j <= 2 * m&&a[j].x == i)
			typ = a[j].typ, add(root[j], root[j - 1], root[0], 1, n, a[j].dw, a[j].up), j ++;
		addedge(2 + i, t, 1), addedge(s, root[j - 1], 1);
	}
	printf("%d\n", Maxflow :: maxflow());

	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10050;
const int D = 600050;
const int M = 2500050;
const int INF = 20000000;

int s, t, nds;
namespace Maxflow{
	int dis[D], q[M], f, r, tot = 1;
	int head[D], to[M], nex[M], fl[M], now[D];
	void addedge(int u, int v, int f){
		tot++; to[tot]=v, nex[tot]=head[u], head[u]=tot, fl[tot]=f;
		tot++; to[tot]=u, nex[tot]=head[v], head[v]=tot, fl[tot]=0;
	}
	bool spfa(){
		int i, j;
		q[f = r = 1] = s;
		for(i = 2; i <= nds; i ++)
			dis[i] = INF;
		while(f <= r){
			j = q[f ++];
			for(i = head[j]; i; i = nex[i]){
				if(fl[i] && dis[to[i]] > dis[j] + 1){
					dis[to[i]] = dis[j] + 1;
					q[++r] = to[i];
				}
			}
		}
		return dis[t] != INF;
	}
	int dinic(int x, int flow){
		if(x == t)
			return flow;
		for(int i = now[x]; i; now[x] = i = nex[i]){
			if(fl[i] && dis[x] + 1 == dis[to[i]]){
				int tmp = dinic(to[i], min(fl[i], flow));
				if(tmp != 0){
					fl[i] -= tmp;
					fl[i^1] += tmp;
					return tmp;
				}
			}
		}
		return 0;
	}
	int maxflow(){
		int i, j, ans = 0;
		while(spfa()){
			for(i = 1; i <= nds; i ++)
				now[i] = head[i];
			int tmp = 0;
			while(tmp = dinic(s, INF))
				ans += tmp;
		}
		return ans;
	}
}
using Maxflow :: addedge;

//s, t, col, row, tre
int lc[M], rc[M], root[2 * N], typ;
void build(int &p, int l, int r){
	p = ++nds;
	if(l == r){
		addedge(p, 2 + l, INF);
		return;
	}
	int md = l + r >>1;
	build(lc[p], l, md);
	build(rc[p], md + 1, r);
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}
void add(int &p, int las, int one, int l, int r, int x, int y){
//typ == 1:break
//typ == -1:recover
	p = ++ nds;
	if(l == x&&r == y){
		if(typ == -1){
			if(x == y)
				addedge(p, 2 + x, INF);
			else{
				lc[p] = lc[one];
				rc[p] = rc[one];
				addedge(p, lc[p], INF);
				addedge(p, rc[p], INF);
			}
		}
		return;
	}
	lc[p] = lc[las];
	rc[p] = rc[las];
	int md = l + r >>1;
	if(y <= md)
		add(lc[p], lc[las], lc[one], l, md, x, y);
	else if(x > md)
		add(rc[p], rc[las], rc[one], md + 1, r, x, y);
	else{
		add(lc[p], lc[las], lc[one], l, md, x, md);
		add(rc[p], rc[las], rc[one], md + 1, r, md + 1, y);
	}
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}

namespace io{
	const int L = (1 << 19) + 1;
	char ibuf[L], *iS, *iT, c; int f;
	inline char gc(){
		if(iS == iT){
			iT = (iS = ibuf) + fread(ibuf, 1, L, stdin);
			return iS == iT ? EOF : *iS ++;
		}
		return *iS ++;
	}
	template <class I> void read(I &x){
		for(f = 1, c = gc(); c < '0'||c > '9'; c = gc())
			if(c == '-')
				f = -1;
		for(x = 0; c <= '9'&&c >= '0'; c = gc())
			x = x * 10 + (c&15);
		x *= f;
	}
};
using io :: read;
struct line{
	int dw, up, typ, x;
	line(int xx = 0, int y1 = 0, int y2 = 0, int tp = 0){
		x = xx;
		dw = y1;
		up = y2;
		typ = tp;
	}
	inline bool operator < (const line &b)const{
		if(x == b.x)
			return typ < b.typ;
		return x < b.x;
	}
}a[2 * N];
int n, m;

int main(){

	int i, j, k, p, q;
	s = 1, read(n), read(m);
	t = 2, nds = 2 + n;
	build(root[0], 1, n);
	for(i = 1; i <= m; i ++){
		read(j), read(k), read(p), read(q);
		a[2 * i - 1] = line(k, j, p, 1);
		a[2 * i] = line(q + 1, j, p, -1);
	}
	sort(a + 1, a + 2*m + 1);
	for(i = j = 1; i <= n; i ++){
		while(j <= 2 * m&&a[j].x == i)
			typ = a[j].typ, add(root[j], root[j - 1], root[0], 1, n, a[j].dw, a[j].up), j ++;
		addedge(2 + i, t, 1), addedge(s, root[j - 1], 1);
	}
	printf("%d\n", Maxflow :: maxflow());

	return 0;
}

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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXN 50005

int n,q,S,T,top = 1,dep[MAXN],to[MAXM],wei[MAXM],nxt[MAXM],cur[MAXN],head[MAXN];
void Add_Edge (int u,int v,int w){
	to[++ top] = v,wei[top] = w,nxt[top] = head[u],head[u] = top;
	to[++ top] = u,wei[top] = 0,nxt[top] = head[v],head[v] = top;
}
bool BFS (){
	for (Int i = 1;i <= T;++ i) cur[i] = head[i],dep[i] = INF;
	queue <int> q;while (!q.empty()) q.pop ();dep[S] = 0,q.push (S);
	while (!q.empty()){
		int u = q.front();q.pop ();
		for (Int i = head[u];i;i = nxt[i]){
			int v = to[i],w = wei[i];
			if (dep[v] == INF && w){
				dep[v] = dep[u] + 1;
				q.push (v);
			}
		}
	}
	return dep[T] != INF;
}
int dfs (int u,int lim){
	if (u == T) return lim;
	int used = 0;
	for (Int i = cur[u];i && lim;i = nxt[i]){
		cur[u] = i;int v = to[i],w = wei[i];
		if (dep[v] == dep[u] + 1 && w){
			int del = dfs (v,min (lim,w));
			used += del,lim -= del,wei[i] -= del,wei[i ^ 1] += del;
		}
	}
	if (used == 0) dep[u] = INF;
	return used;
}
int Dinic (){
	int res = 0;
	while (BFS ()) res += dfs (S,INF);
	return res;
}
int sizt;
int id (int l,int r,int type){return type * sizt + ((l + r) | (l != r));}
void build (int l,int r,int type){
	int rt = id (l,r,type);
	if (l == r){
		if (type == 0) Add_Edge (S,rt,1);
		if (type == 1) Add_Edge (rt,T,1);
		return ;
	}
	int mid = (l + r) >> 1;
	build (l,mid,type),build (mid + 1,r,type);
	if (type == 0) Add_Edge (id (l,mid,type),rt,INF),Add_Edge (id (mid + 1,r,type),rt,INF);
	else Add_Edge (rt,id (l,mid,type),INF),Add_Edge (rt,id (mid + 1,r,type),INF);
}

#define PII pair<int,int>
#define MP make_pair

void modify (int l,int r,int type,int tl,int tr,vector<PII> &s){
	if (l >= tl && r <= tr) return s.push_back (MP (id (l,r,type),r - l + 1));
	int mid = (l + r) >> 1;
	if (tl <= mid) modify (l,mid,type,tl,tr,s);
	if (tr > mid) modify (mid + 1,r,type,tl,tr,s);
}
void update (int tl,int tr,int ql,int qr){
	static vector <PII> A,B;A.clear(),B.clear();
	modify (1,n,0,tl,tr,A),modify (1,n,1,ql,qr,B);
	for (Int i = 0;i < A.size();++ i) for (Int j = 0;j < B.size();++ j) Add_Edge (A[i].first,B[j].first,A[i].second * B[j].second);
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int lef[MAXN];
void Scan (){//输入+扫描线
	read (n,q);int tot = 0;
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	sort (mat + 1,mat + tot + 1);
	sizt = id (n,n,0);
	S = sizt * 2 + 3,T = S + 1;
	build (1,n,0),build (1,n,1);
	for (Int i = 1;i <= tot;++ i){
		if (mat[i].type == 1) for (Int j = mat[i].r1;j <= mat[i].r2;++ j) lef[j] = mat[i].c;
		else{
			for (Int j = mat[i].r1,lasw = mat[i].c - 1,lash = 0;j <= mat[i].r2 + 1;++ j){
				if (j == mat[i].r2 + 1 || lef[j] ^ lasw){
					if (lasw + 1 < mat[i].c) update (lash,j - 1,lasw + 1,mat[i].c - 1);
					lasw = lef[j],lash = j;
				}
			}
		}
	}
}

signed main(){
	Scan ();
	write (Dinic ()),putchar ('\n');
	return 0;
}

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}

	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
typedef std::pair<int,int> pii;
#define fi first
#define se second

cs int N=1e4+7,INF=0x3f3f3f3f;

int n;

int S=1,T=2,tot=3;
namespace NetWork{
	cs int N=::N<<2|1;
	struct edge{
		int to,rev,cap;
		edge(int _to,int _rev,int _cap):to(_to),rev(_rev),cap(_cap){}
	};
	typedef std::vector<edge>::iterator iter;
	std::vector<edge> G[N];iter cur[N];
	inline void adde(int u,int v,int cap){
		//cerr<<"adde : "<<u<<" "<<v<<" "<<cap<<"\n";
		G[u].push_back((edge){v,G[v].size(),cap});
		G[v].push_back((edge){u,G[u].size()-1,0});
	}

	int lev[N],gap[N],finished;
	inline void BFS(){
		memset(lev,0,sizeof lev);
		memset(gap,0,sizeof gap);
		std::queue<int> q;q.push(T);lev[T]=gap[1]=1;
		while(!q.empty()){
			int u=q.front();q.pop();cur[u]=G[u].begin();
			for(iter e=G[u].begin();e!=G[u].end();++e)
			if(!lev[e->to]){
				lev[e->to]=lev[u]+1;
				++gap[lev[e->to]];
				q.push(e->to);
			}
		}
		finished=lev[S]==0;
	}

	int dfs(int u,int flow){
		if(u==T)return flow;
		int ans=0;
		for(iter &e=cur[u];e!=G[u].end();++e)
		if(e->cap&&lev[e->to]+1==lev[u]){
			int delta=dfs(e->to,std::min(flow-ans,e->cap));
			if(delta){
				e->cap-=delta;
				G[e->to][e->rev].cap+=delta;
				if((ans+=delta)==flow)return ans;
			}
		}
		if(!--gap[lev[u]])finished=true;
		++gap[++lev[u]];cur[u]=G[u].begin();
		return ans;
	}
	inline int Flow(){
		int flow=0;BFS();
		while(!finished)flow+=dfs(S,INF);
		return flow;
	}
}
using NetWork::adde;

int nd[N],siz[N],ct;

int pc;
int lc[N<<2|1],rc[N<<2|1];
namespace Row{//u,d
	int rt;
	inline void build(int &u,int l,int r){
		u=++tot;if(l==r){adde(u,T,1);++pc;return ;}
		int mid=l+r>>1;
		build(lc[u],l,mid);
		build(rc[u],mid+1,r);
		adde(u,lc[u],mid-l+1);
		adde(u,rc[u],r-mid);
	}
	inline void add(int u,int l,int r,int ql,int qr){//adde v to u
		if(ql<=l&&r<=qr){
			int usiz=r-l+1;
			for(int re i=1;i<=ct;++i){
				int v=nd[i],vsiz=siz[i];
				adde(v,u,std::min(vsiz,usiz));
			}
			return ;
		}
		int mid=l+r>>1;
		if(ql<=mid)add(lc[u],l,mid,ql,qr);
		if(mid<qr)add(rc[u],mid+1,r,ql,qr);
	}
}

namespace Col{//l,r
	int rt;
	inline void build(int &u,int l,int r){
		u=++tot;if(l==r){adde(S,u,1);return ;}
		int mid=l+r>>1;
		build(lc[u],l,mid);
		build(rc[u],mid+1,r);
		adde(lc[u],u,mid-l+1);
		adde(rc[u],u,r-mid);
	}
	inline void get(int u,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr){nd[++ct]=u,siz[ct]=r-l+1;return ;}
		int mid=l+r>>1;
		if(ql<=mid)get(lc[u],l,mid,ql,qr);
		if(mid<qr)get(rc[u],mid+1,r,ql,qr);
	}
}

inline void work_edge(int l,int r,int u,int d){
	if(l>r||u>d)return ;ct=0;
	Col::get(Col::rt,1,n,l,r);
	Row::add(Row::rt,1,n,u,d);
}

struct seg{
	int l,r,las;
	seg(int _l,int _r,int _t=-1):l(_l),r(_r),las(_t){}
	friend bool operator<(cs seg &a,cs seg &b){
		return a.l<b.l;
	}
};

std::set<seg> s;
typedef std::set<seg>::iterator it_s;
template<class T>T pre(T x){return --x;}
template<class T>T nxt(T x){return ++x;}
std::vector<seg> del[N],rec[N];

signed main(){
#ifdef zxyoi
	freopen("chess.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("chess.in","r",stdin);freopen("chess.out","w",stdout);
#endif
#endif
	n=gi();int m=gi();
	Row::build(Row::rt,1,n);
	Col::build(Col::rt,1,n);
	while(m--){
		int l=gi(),u=gi(),r=gi(),d=gi();
		del[l].push_back(seg(u,d));
		rec[r+1].push_back(seg(u,d));
	}
	s.insert(seg(1,n,1));s.insert(seg(n+1,n+1,n+2));
	for(int re i=1;i<=n+1;++i){
		for(int re j=0;j<rec[i].size();++j){
			int L=rec[i][j].l,R=rec[i][j].r;
			it_s t=--s.upper_bound(rec[i][j]);
			if(t!=s.begin()){
				it_s l=pre(t);
				if(l->las!=n+2){
					work_edge(l->las,i-1,l->l,l->r);
					L=l->l;s.erase(l);
				}
			}
			if(nxt(t)!=s.end()){
				it_s r=nxt(t);
				if(r->las!=n+2){
					work_edge(r->las,i-1,r->l,r->r);
					R=r->r;s.erase(r);
				}
			}
			s.erase(t);
			s.insert(seg(L,R,i));
		}
		for(int re j=0;j<del[i].size();++j){
			int L=del[i][j].l,R=del[i][j].r;
			it_s t=--s.upper_bound(del[i][j]);
			work_edge(t->las,i-1,t->l,t->r);
			int l=t->l,r=t->r;s.erase(t);
			if(l!=L)s.insert((seg){l,L-1,i});
			if(r!=R)s.insert((seg){R+1,r,i});
			s.insert(seg(L,R,n+2));
		}
	}
	for(it_s i=s.begin();i!=s.end();++i)work_edge(i->las,n,i->l,i->r);
	cout<<NetWork::Flow()<<"\n";
	return 0;
}

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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXED 5000005
#define MAXN 50005

int n,q,S,T,top = 1,dep[MAXN],head[MAXN];
struct Edge { int to, flow, nxt; } graph[MAXED * 2 + 5];

inline void Add_Edge ( const int s, const int t, const int f ) {
	graph[++ top] = { t, f, head[s] };head[s] = top;
	graph[++ top] = { s, 0, head[t] };head[t] = top;
}

inline int DFS ( const int u, int iflow ) {
	if ( u == T ) return iflow;
	int oflow = 0;
	for ( int i = head[u], v, of; i; i = graph[i].nxt ) {
		if ( dep[v = graph[i].to] == dep[u] + 1 && graph[i].flow ) {
			of = DFS ( v, std::min ( iflow, graph[i].flow ) );
			oflow += of, graph[i].flow -= of, graph[i ^ 1].flow += of;
			if ( ! ( iflow -= of ) ) break;
		}
	}
	if ( ! oflow ) dep[u] = -1;
	return oflow;
}

bool BFS (){
	for (Int i = 1;i <= T;++ i) dep[i] = -1;
	queue <int> q;dep[S] = 0,q.push (S);
	while (!q.empty()){
		int u = q.front();q.pop ();
		for (Int i = head[u];i;i = graph[i].nxt){
			int v = graph[i].to,w = graph[i].flow;
			if (dep[v] == -1 && w){
				dep[v] = dep[u] + 1;
				q.push (v);
			}
		}
	}
	return dep[T] != -1;
}

inline int calc () {
	int ret = 0;
	for ( ; BFS (); ret += DFS ( S, INF ) ) ;
	return ret;
}

int sizt;

typedef std::vector<std::pair<int, int> > vecpii;

inline int id ( const int l, const int r, const bool type ) {
	return type * sizt + ( ( l + r ) | ( l != r ) );
}

inline void build ( const int l, const int r, const bool type ) {
	int rt = id ( l, r, type ), mid = l + r >> 1;
	if ( l == r ) {
		if ( ! type ) Add_Edge ( S, rt, 1 );
		else Add_Edge ( rt, T, 1 );
		return ;
	}
	if ( ! type ) {
		Add_Edge ( id ( l, mid, type ), rt, INF );
		Add_Edge ( id ( mid + 1, r, type ), rt, INF );
	} else {
		Add_Edge ( rt, id ( l, mid, type ), INF );
		Add_Edge ( rt, id ( mid + 1, r, type ), INF );
	}
	build ( l, mid, type ), build ( mid + 1, r, type );
}

inline void modify ( const int l, const int r, const int el, const int er, const bool type, vecpii& vec ) {
	int rt = id ( l, r, type ), mid = l + r >> 1;
	if ( el <= l && r <= er ) return vec.push_back ( { rt, r - l + 1 } );
	if ( el <= mid ) modify ( l, mid, el, er, type, vec );
	if ( mid < er ) modify ( mid + 1, r, el, er, type, vec );
}

inline void init () {
	sizt = id ( n, n, 0 );
	S = sizt * 2 + 3, T = S + 1;
	build ( 1, n, 0 ), build ( 1, n, 1 );
}

inline void update ( const int rl, const int rr, const int cl, const int cr ) {
	static vecpii R, C; R.clear (), C.clear ();
	modify ( 1, n, rl, rr, 0, R ), modify ( 1, n, cl, cr, 1, C );
	for ( auto r: R ) for ( auto c: C ) Add_Edge ( r.first, c.first, r.second * c.second );
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int tot,lef[MAXN];
inline void part () {
	std::sort ( mat + 1, mat + tot + 1 );
	for ( int i = 1; i <= tot; ++ i ) {
		if ( mat[i].type ) {
			for ( int j = mat[i].r1; j <= mat[i].r2; ++ j ) {
				lef[j] = mat[i].c;
			}
		} else {
			for ( int j = mat[i].r1, lasw = mat[i].c - 1, lash = 0; j <= mat[i].r2 + 1; ++ j ) {
				if ( j == mat[i].r2 + 1 || lef[j] ^ lasw ) {
					if ( lasw + 1 < mat[i].c ) {
						update ( lash, j - 1, lasw + 1, mat[i].c - 1 );
					}
					lasw = lef[j], lash = j;
				}
			}
		}
	}
}
void Scan (){//输入+扫描线
	read (n,q);
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	init (),part ();
}

signed main(){
	Scan ();
	write (calc ()),putchar ('\n');
	return 0;
}

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 10010
#define M 80000
#define INF 1e9
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int& x){
    char c=nc();
    for(;c<'0'||c>'9';c=nc());
    for(x=0;c>='0'&&c<='9';x=(x<<3)+(x<<1)+c-48,c=nc());
}
struct Node{
    int x,l,r;
    int f;
    Node(int x=0,int l=0,int r=0,int f=0):x(x),l(l),r(r),f(f){}
}c[N<<1];
struct Edge{
    int t,nx,c;
}e[3000000];
int h[M],d[M],q[M],cur[M];
bool b[M];
int i,j,k,n,m,p,a[N];
int x1,x2,y1,y2,s,t,cnt;
int num=1,r1,r2,ls[N<<3],rs[N<<3];
inline void Add(int x,int y,int z){
    e[++num].t=y;e[num].c=z;e[num].nx=h[x];h[x]=num;
    e[++num].t=x;e[num].c=0;e[num].nx=h[y];h[y]=num;
}
inline void Build(int& x,int y,int l,int r,int d){
    x=++cnt;
    if(y){
        if(!d)Add(x,y,INF);else Add(y,x,INF);
    }
    if(l==r)return;
    int Mid=l+r>>1;
    Build(ls[x],x,l,Mid,d);Build(rs[x],x,Mid+1,r,d);
}
inline void Build2(int x,int l,int r,int y){
    if(l==r){
        if(!y)Add(0,x,1);else Add(x,t,1);
        return;
    }
    int Mid=l+r>>1;
    Build2(ls[x],l,Mid,y);Build2(rs[x],Mid+1,r,y);
}
inline bool Cmp(Node a,Node b){
    if(a.x==b.x)return a.f>b.f;
    return a.x<b.x;
}
inline void Query(int x,int l,int r,int L,int R,int y,bool d){
    if(!x||l>R||r<L)return;
    if(l>=L&&r<=R){
        if(!d)Add(x,y,INF);else Add(y,x,INF);
        return;
    }
    int Mid=l+r>>1;
    Query(ls[x],l,Mid,L,R,y,d);
    Query(rs[x],Mid+1,r,L,R,y,d);
}
inline void Insert(int x,int l,int r){
    int pos=-1,last=-1,y;
    for(int i=l;i<=r+1;i++){
        if(i<=r)y=a[i]+1,a[i]=-1;
        if(y!=last||i==r+1){
            if(pos!=-1){
                int z=++cnt;
                Query(r1,1,n,pos,i-1,z,0);
                Query(r2,1,n,last,x,z,1);
            }
            pos=i;last=y;
        }
    }
}
inline void Modify(int x,int l,int r){
    for(int i=l;i<=r;i++)a[i]=x;
}
inline bool Bfs(){
    memset(b,0,sizeof(b));
    b[s]=1;int l=0,r=1;q[1]=s;
    while(++l<=r){
        int x=q[l];
        for(int i=h[x];i;i=e[i].nx)
        if(e[i].c&&!b[e[i].t]){
            b[e[i].t]=1;d[e[i].t]=d[x]+1;q[++r]=e[i].t;
        }
    }
    return b[t];
}
inline int Dfs(int x,int y){
    if(!y||x==t)return y;
    int k=y,f;
    for(int& i=cur[x];i;i=e[i].nx){
        int v=e[i].t;
        if(d[v]==d[x]+1&&(f=Dfs(v,min(y,e[i].c)))){
            y-=f;e[i].c-=f;e[i^1].c+=f;
            if(!y)break;
        }
    }
    if(k==y)d[x]=-1;
    return k-y;
}
inline int Dinic(){
    int Ans=0;
    while(Bfs()){
        for(int i=0;i<=cnt;i++)cur[i]=h[i];
        Ans+=Dfs(s,INF);
    }
    return Ans;
}
int main(){
    Read(n);Read(m);
    Build(r1,0,1,n,0);Build(r2,0,1,n,1);
    for(i=1;i<=m;i++){
        Read(x1);Read(y1);Read(x2);Read(y2);
        c[i*2-1]=Node(x1-1,y1,y2,0);c[i*2]=Node(x2,y1,y2,1);
    }
    sort(c+1,c+m*2+1,Cmp);
    for(i=1;i<=m*2;i++)
    if(!c[i].f)Insert(c[i].x,c[i].l,c[i].r);else Modify(c[i].x,c[i].l,c[i].r);
    Insert(n,1,n);
    t=++cnt;
    Build2(r1,1,n,s);Build2(r2,1,n,t);
    printf("%d\n",Dinic());
    return 0;
}

#include <queue>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <algorithm>
using namespace std;
#define mid ((l+r)>>1)
#define rep(x, a, b) for(int x=a; x<=b; x++)
typedef pair<int, int> pii;
const int N=300000;
const int E=1000000;
queue<int> q;
int head[N], cur[N], cnt, size, Ql, Qr, n, Q, t, tar, lev[N], S, T, ls[N], rs[N], root[2];
struct line{
	int y, x1, x2;
	bool operator < (const line &a) const { return y<a.y; }
}I[N], O[N];
struct Edge{
	int to, next, flow;
}edge[E];
void AddEdge(int u, int v, int c){
	edge[cnt]=(Edge){ v, head[u], c }, head[u]=cnt++;
	edge[cnt]=(Edge){ u, head[v], 0 }, head[v]=cnt++;
}
int bfs(){
	memset(lev, 0, sizeof lev);
	q.push(S); lev[S]=1;
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		for(int i=head[u]; ~i; i=edge[i].next)
		{
			int v=edge[i].to;
			if(!lev[v] && edge[i].flow) q.push(v), lev[v]=lev[u]+1;
		}
	}
	return lev[T];
}
int dfs(int u, int in){
	if(u==T || !in) return in;
	int ret, flow=0;
	for(int &i=cur[u]; ~i; i=edge[i].next)
	{
		int v=edge[i].to;
		if(lev[v]==lev[u]+1 && (ret=dfs(v, min(in, edge[i].flow)))>0)
		{
			flow+=ret;
			edge[i].flow-=ret;
			edge[i^1].flow+=ret;
			if(!(in-=ret)) break;
		}
	}
	if(!flow) lev[u]=0;
	return flow;
}
int dinic(){
	int flow=0;
	while(bfs())
	{
		memcpy(cur, head, sizeof cur);
		flow+=dfs(S, 1e9);
	}
	return flow;
}
void build(int &rt, int l, int r){
	rt=++size;
	if(l==r)
	{
		if(t) AddEdge(rt, T, 1);
		else AddEdge(S, rt, 1);
		return;
	}
	build(ls[rt], l, mid);
	build(rs[rt], mid+1, r);
	if(t) AddEdge(rt, ls[rt], 1e9), AddEdge(rt, rs[rt], 1e9);
	else AddEdge(ls[rt], rt, 1e9), AddEdge(rs[rt], rt, 1e9);
}
void Add(int rt, int l, int r){
	if(l>=Ql && r<=Qr)
	{
		if(t) AddEdge(tar, rt, 1e9);
		else AddEdge(rt, tar, 1e9);
		return;
	}
	if(Ql<=mid) Add(ls[rt], l, mid);
	if(Qr> mid) Add(rs[rt], mid+1, r);
}
void Add(int l, int r, int u, int d){
	tar=++size;
	Ql=l, Qr=r; t=0;
	Add(root[0], 1, n);
	Ql=u, Qr=d; t=1;
	Add(root[1], 1, n);
}
struct data{
	int u, d, las;
	data(){}
	data(int _, int __, int ___):u(_),d(__),las(___){}
}tag[N], val;
void down(int rt){
	if(tag[rt].las==-1) return;
	tag[rt<<1]=tag[rt<<1|1]=tag[rt];
	tag[rt].las=-1;
}
void update(int rt, int l, int r){
	if(l>=Ql && r<=Qr){ tag[rt]=val; return; }
	down(rt);
	if(Ql<=mid) update(rt<<1, l, mid);
	if(Qr> mid) update(rt<<1|1, mid+1, r);
}
void query(int rt, int l, int r, int p){
	if(~tag[rt].las){ val=tag[rt]; return; }
	if(l==r){ val.las=-1; return; }
	if(p<=mid) query(rt<<1, l, mid, p);
	else query(rt<<1|1, mid+1, r, p);
}
int main(){
	S=0; T=1; size=1;
	scanf("%d%d", &n, &Q);
	memset(head, -1, sizeof head);
	t=0; build(root[0], 1, n);
	t=1; build(root[1], 1, n);
	rep(i, 1, Q)
	{
		int x1, x2, y1, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		I[i]=(line){ y1, x1, x2 };
		O[i]=(line){ y2+1, x1, x2 };
	}
	++Q;
	I[Q]=(line){n+1, 1, n};
	O[Q]=(line){n+2, 1, n};
	sort(I+1, I+Q+1);
	sort(O+1, O+Q+1);
	int pi=1, po=1;
	rep(i, 0, N-1) tag[i].las=-1;
	Ql=1, Qr=n;
	val=(data){1, n, 1};
	update(1, 1, n);
	rep(y, 1, n+1)
	{
		while(po<=Q && O[po].y==y)
		{
			int x1=O[po].x1, x2=O[po].x2;
			if(x1>1)
			{
				query(1, 1, n, x1-1);
				if(~val.las)
				{
					x1=val.u;
					if(val.las<y) Add(val.las, y-1, val.u, val.d);
					Ql=val.u, Qr=val.d; val.las=-1;
					update(1, 1, n);
				}
			}
			if(x2<n)
			{
				query(1, 1, n, x2+1);
				if(~val.las)
				{
					x2=val.d;
					if(val.las<y) Add(val.las, y-1, val.u, val.d);
					Ql=val.u, Qr=val.d; val.las=-1;
					update(1, 1, n);
				}
			}
			Ql=x1, Qr=x2; val=(data){ x1, x2, y };
			update(1, 1, n);
			po++;
		}
		while(pi<=Q && I[pi].y==y)
		{
			int x1=I[pi].x1, x2=I[pi].x2;
			query(1, 1, n, x1);
			if(val.las<y) Add(val.las, y-1, val.u, val.d);
			data tmp=val;
			Ql=val.u, Qr=val.d; val.las=-1;
			update(1, 1, n);
			if(tmp.u<x1)
			{
				Ql=tmp.u, Qr=x1-1; val=(data){ Ql, Qr, y };
				update(1, 1, n);
			}
			if(tmp.d>x2)
			{
				Ql=x2+1, Qr=tmp.d; val=(data){ Ql, Qr, y };
				update(1, 1, n);
			}
			pi++;
		}
	}
	printf("%d\n", dinic());
}

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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXED 5000005
#define MAXN 50005

int n,q,S,T,top = 1,dep[MAXN],head[MAXN];
struct Edge { int to, flow, nxt; } graph[MAXED * 2 + 5];

inline void Add_Edge ( const int s, const int t, const int f ) {
	graph[++ top] = { t, f, head[s] };head[s] = top;
	graph[++ top] = { s, 0, head[t] };head[t] = top;
}

inline int DFS ( const int u, int iflow ) {
	if ( u == T ) return iflow;
	int oflow = 0;
	for ( int i = head[u], v, of; i; i = graph[i].nxt ) {
		if ( dep[v = graph[i].to] == dep[u] + 1 && graph[i].flow ) {
			of = DFS ( v, std::min ( iflow, graph[i].flow ) );
			oflow += of, graph[i].flow -= of, graph[i ^ 1].flow += of;
			if ( ! ( iflow -= of ) ) break;
		}
	}
	if ( ! oflow ) dep[u] = -1;
	return oflow;
}

inline bool BFS () {
	static std::queue<int> que;
	for ( int i = 1; i <= T; ++ i ) dep[i] = -1;
	que.push ( S ), dep[S] = 0;
	for ( int u; ! que.empty (); que.pop () ) {
		u = que.front ();
		for ( int i = head[u], v; i; i = graph[i].nxt ) {
			if ( ! ~ dep[v = graph[i].to] && graph[i].flow ) {
				que.push ( v ), dep[v] = dep[u] + 1;
			}
		}
	}
	return ~ dep[T];
}

inline int calc () {
	int ret = 0;
	for ( ; BFS (); ret += DFS ( S, INF ) ) ;
	return ret;
}

int sizt;

typedef std::vector<std::pair<int, int> > vecpii;

inline int id ( const int l, const int r, const bool type ) {
	return type * sizt + ( ( l + r ) | ( l != r ) );
}

inline void build ( const int l, const int r, const bool type ) {
	int rt = id ( l, r, type ), mid = l + r >> 1;
	if ( l == r ) {
		if ( ! type ) Add_Edge ( S, rt, 1 );
		else Add_Edge ( rt, T, 1 );
		return ;
	}
	if ( ! type ) {
		Add_Edge ( id ( l, mid, type ), rt, INF );
		Add_Edge ( id ( mid + 1, r, type ), rt, INF );
	} else {
		Add_Edge ( rt, id ( l, mid, type ), INF );
		Add_Edge ( rt, id ( mid + 1, r, type ), INF );
	}
	build ( l, mid, type ), build ( mid + 1, r, type );
}

inline void modify ( const int l, const int r, const int el, const int er, const bool type, vecpii& vec ) {
	int rt = id ( l, r, type ), mid = l + r >> 1;
	if ( el <= l && r <= er ) return vec.push_back ( { rt, r - l + 1 } );
	if ( el <= mid ) modify ( l, mid, el, er, type, vec );
	if ( mid < er ) modify ( mid + 1, r, el, er, type, vec );
}

inline void init () {
	sizt = id ( n, n, 0 );
	S = sizt * 2 + 3, T = S + 1;
	build ( 1, n, 0 ), build ( 1, n, 1 );
}

inline void update ( const int rl, const int rr, const int cl, const int cr ) {
	static vecpii R, C; R.clear (), C.clear ();
	modify ( 1, n, rl, rr, 0, R ), modify ( 1, n, cl, cr, 1, C );
	for ( auto r: R ) for ( auto c: C ) Add_Edge ( r.first, c.first, r.second * c.second );
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int tot,lef[MAXN];
inline void part () {
	std::sort ( mat + 1, mat + tot + 1 );
	for ( int i = 1; i <= tot; ++ i ) {
		if ( mat[i].type ) {
			for ( int j = mat[i].r1; j <= mat[i].r2; ++ j ) {
				lef[j] = mat[i].c;
			}
		} else {
			for ( int j = mat[i].r1, lasw = mat[i].c - 1, lash = 0; j <= mat[i].r2 + 1; ++ j ) {
				if ( j == mat[i].r2 + 1 || lef[j] ^ lasw ) {
					if ( lasw + 1 < mat[i].c ) {
						update ( lash, j - 1, lasw + 1, mat[i].c - 1 );
					}
					lasw = lef[j], lash = j;
				}
			}
		}
	}
}
void Scan (){//输入+扫描线
	read (n,q);
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	init (),part ();
}

signed main(){
	Scan ();
	write (calc ()),putchar ('\n');
	return 0;
}

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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXN 50005

int n,q,S,T,top = 1,to[MAXM],wei[MAXM],nxt[MAXM],head[MAXM];
void Add_Edge (int u,int v,int w){
	to[++ top] = v,wei[top] = w,nxt[top] = head[u],head[u] = top;
	to[++ top] = u,wei[top] = 0,nxt[top] = head[v],head[v] = top;
}
int dep[MAXM];
bool BFS (int s,int t){
	for (Int i = 1;i <= T;++ i) dep[i] = INF;
	queue <int> q;while (!q.empty()) q.pop ();dep[s] = 0,q.push (s);
	while (!q.empty()){
		int u = q.front();q.pop ();
		for (Int i = head[u];i;i = nxt[i]){
			int v = to[i],w = wei[i];
			if (dep[v] == INF && w){
				dep[v] = dep[u] + 1;
				q.push (v);
			}
		}
	}
	return dep[t] != INF;
}
int dfs (int s,int t,int lim){
	if (s == t) return lim;
	int used = 0;
	for (Int i = head[s];i && lim;i = nxt[i]){
		int v = to[i],w = wei[i];
		if (dep[v] == dep[s] + 1 && w){
			int del = dfs (v,t,min (lim,w));
			used += del,lim -= del,wei[i] -= del,wei[i ^ 1] += del;
		}
	}
	if (used == 0) dep[s] = INF;
	return used;
}
int Dinic (int s,int t){
	int res = 0;
	while (BFS (s,t)) res += dfs (s,t,INF);
	return res;
}
int sizt;
int id (int l,int r,int type){return type * sizt + ((l + r) | (l != r));}
void build (int l,int r,int type){
	int rt = id (l,r,type);
	if (l == r){
		if (type == 0) Add_Edge (S,rt,1);
		if (type == 1) Add_Edge (rt,T,1);
		return ;
	}
	int mid = (l + r) >> 1;
	build (l,mid,type),build (mid + 1,r,type);
	if (type == 0) Add_Edge (id (l,mid,type),rt,INF),Add_Edge (id (mid + 1,r,type),rt,INF);
	else Add_Edge (rt,id (l,mid,type),INF),Add_Edge (rt,id (mid + 1,r,type),INF);
}

#define PII pair<int,int>
#define MP make_pair

void modify (int l,int r,int type,int tl,int tr,vector<PII> &s){
	if (l >= tl && r <= tr) return s.push_back (MP (id (l,r,type),r - l + 1));
	int mid = (l + r) >> 1;
	if (tl <= mid) modify (l,mid,type,tl,tr,s);
	if (tr > mid) modify (mid + 1,r,type,tl,tr,s);
}
void update (int tl,int tr,int ql,int qr){
	static vector <PII> A,B;A.clear(),B.clear();
	modify (1,n,0,tl,tr,A),modify (1,n,1,ql,qr,B);
	for (Int i = 0;i < A.size();++ i) for (Int j = 0;j < B.size();++ j) Add_Edge (A[i].first,B[j].first,A[i].second * B[j].second);
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int lef[MAXN];
void Scan (){//输入+扫描线
	read (n,q);int tot = 0;
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	sort (mat + 1,mat + tot + 1);
	sizt = id (n,n,0);
	S = sizt * 2 + 3,T = S + 1;
	build (1,n,0),build (1,n,1);
	for (Int i = 1;i <= tot;++ i){
		if (mat[i].type == 1) for (Int j = mat[i].r1;j <= mat[i].r2;++ j) lef[j] = mat[i].c;
		else{
			for (Int j = mat[i].r1,lasw = mat[i].c - 1,lash = 0;j <= mat[i].r2 + 1;++ j){
				if (j == mat[i].r2 + 1 || lef[j] ^ lasw){
					if (lasw + 1 < mat[i].c) update (lash,j - 1,lasw + 1,mat[i].c - 1);
					lasw = lef[j],lash = j;
				}
			}
		}
	}
}

signed main(){
	Scan ();
	write (Dinic (S,T)),putchar ('\n');
	return 0;
}

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

#define Int register int
#define INF 0x3f3f3f3f
#define MAXM 10000005
#define MAXED 5000005
#define MAXN 50005

int n,q,S,T,top = 1,dep[MAXN],head[MAXN];
struct Edge { int to, flow, nxt; } graph[MAXED * 2 + 5];
inline void link ( const int s, const int t, const int f ) {
	graph[++ top] = { t, f, head[s] };
	head[s] = top;
}

inline void Add_Edge ( const int s, const int t, const int f ) {
#ifdef LOCAL_DEBUG
	printf ( "%dep %dep %dep\n", s, t, f );
#endif
	link ( s, t, f ), link ( t, s, 0 );
}

inline int DFS ( const int u, int iflow ) {
	if ( u == T ) return iflow;
	int oflow = 0;
	for ( int i = head[u], v, of; i; i = graph[i].nxt ) {
		if ( dep[v = graph[i].to] == dep[u] + 1 && graph[i].flow ) {
			of = DFS ( v, std::min ( iflow, graph[i].flow ) );
			oflow += of, graph[i].flow -= of, graph[i ^ 1].flow += of;
			if ( ! ( iflow -= of ) ) break;
		}
	}
	if ( ! oflow ) dep[u] = -1;
	return oflow;
}

inline bool BFS () {
	static std::queue<int> que;
	for ( int i = 1; i <= T; ++ i ) dep[i] = -1;
	que.push ( S ), dep[S] = 0;
	for ( int u; ! que.empty (); que.pop () ) {
		u = que.front ();
		for ( int i = head[u], v; i; i = graph[i].nxt ) {
			if ( ! ~ dep[v = graph[i].to] && graph[i].flow ) {
				que.push ( v ), dep[v] = dep[u] + 1;
			}
		}
	}
	return ~ dep[T];
}

inline int calc () {
	int ret = 0;
	for ( ; BFS (); ret += DFS ( S, INF ) ) ;
	return ret;
}

int sizt;

typedef std::vector<std::pair<int, int> > vecpii;

inline int id ( const int l, const int r, const bool type ) {
	return type * sizt + ( ( l + r ) | ( l != r ) );
}

inline void build ( const int l, const int r, const bool type ) {
	int rt = id ( l, r, type ), mid = l + r >> 1;
	if ( l == r ) {
		if ( ! type ) Add_Edge ( S, rt, 1 );
		else Add_Edge ( rt, T, 1 );
		return ;
	}
	if ( ! type ) {
		Add_Edge ( id ( l, mid, type ), rt, INF );
		Add_Edge ( id ( mid + 1, r, type ), rt, INF );
	} else {
		Add_Edge ( rt, id ( l, mid, type ), INF );
		Add_Edge ( rt, id ( mid + 1, r, type ), INF );
	}
	build ( l, mid, type ), build ( mid + 1, r, type );
}

inline void modify ( const int l, const int r, const int el, const int er, const bool type, vecpii& vec ) {
	int rt = id ( l, r, type ), mid = l + r >> 1;
	if ( el <= l && r <= er ) return vec.push_back ( { rt, r - l + 1 } );
	if ( el <= mid ) modify ( l, mid, el, er, type, vec );
	if ( mid < er ) modify ( mid + 1, r, el, er, type, vec );
}

inline void init () {
	sizt = id ( n, n, 0 );
	S = sizt * 2 + 3, T = S + 1;
	build ( 1, n, 0 ), build ( 1, n, 1 );
}

inline void update ( const int rl, const int rr, const int cl, const int cr ) {
	static vecpii R, C; R.clear (), C.clear ();
	modify ( 1, n, rl, rr, 0, R ), modify ( 1, n, cl, cr, 1, C );
	for ( auto r: R ) for ( auto c: C ) Add_Edge ( r.first, c.first, r.second * c.second );
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int r1,r2,c;bool type;
	bool operator < (const node &p)const{return c != p.c ? c < p.c : type < p.type;}
}mat[MAXN];
int tot,lef[MAXN];
inline void part () {
	std::sort ( mat + 1, mat + tot + 1 );
	for ( int i = 1; i <= tot; ++ i ) {
		if ( mat[i].type ) {
			for ( int j = mat[i].r1; j <= mat[i].r2; ++ j ) {
				lef[j] = mat[i].c;
			}
		} else {
			for ( int j = mat[i].r1, lasw = mat[i].c - 1, lash = 0; j <= mat[i].r2 + 1; ++ j ) {
				if ( j == mat[i].r2 + 1 || lef[j] ^ lasw ) {
					if ( lasw + 1 < mat[i].c ) {
						update ( lash, j - 1, lasw + 1, mat[i].c - 1 );
					}
					lasw = lef[j], lash = j;
				}
			}
		}
	}
}
void Scan (){//输入+扫描线
	read (n,q);
	for (Int i = 1,r1,r2,c1,c2;i <= q;++ i) read (r1,c1,r2,c2),mat[++ tot] = node {r1,r2,c1,0},mat[++ tot] = node {r1,r2,c2,1};
	mat[++ tot] = node {1,n,n + 1,0},mat[++ tot] = node {1,n,n + 1,1};
	init (),part ();
}

signed main(){
	Scan ();
	write (calc ()),putchar ('\n');
	return 0;
}

/*********************************************************************
 * Source：ysy网络流
 * Problem：CF793G
 * Author：hydd
 * Date：2020/9/4
 * Encoding：Simplified Chinese (GB2312)
*********************************************************************/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<queue>
//#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> pii;
int n,q,s,t,rt[10005];
int cnt,ls[2000005],rs[2000005],tag[2000005];
vector<pii> vec[10005];
int gap[1000005],cur[1000005],d[1000005];
int edgenum,vet[2000005],val[2000005],Next[2000005],Head[1000005];
void addedge(int u,int v,int cost){
    vet[++edgenum]=v; val[edgenum]=cost;
    Next[edgenum]=Head[u]; Head[u]=edgenum;
}
void Add(int u,int v,int f){
	addedge(u,v,f);
	addedge(v,u,0);
}
/*void bfs(){
    memset(gap,0,sizeof(gap));
    memset(d,0,sizeof(d));
    d[t]=1; gap[1]++;
    int head=1,tail=1; que[1]=t;
    while (head<=tail){
        int u=que[head++];
        for (int e=Head[u];e;e=Next[e])
            if (!d[vet[e]]){
                d[vet[e]]=d[u]+1;
                gap[d[vet[e]]]++;
                que[++tail]=vet[e];
            }
    }
}
int dfs(int u,int flow){
    if (u==t) return flow;
    int used=0;
    for (int &e=cur[u];e;e=Next[e])
        if (d[u]==d[vet[e]]+1){
            int tmp=dfs(vet[e],min(flow,val[e]));
            used+=tmp; flow-=tmp;
            val[e]-=tmp; val[e^1]+=tmp;
            if (!flow) return used;
        }
    if (!(--gap[d[u]])) d[s]=cnt+2;
    d[u]++; gap[d[u]]++; cur[u]=Head[u];
    return used;
}
int maxflow(){
    bfs();
    for (int i=0;i<=cnt;i++) cur[i]=Head[i];
    int ans=dfs(s,INF);
    while (d[s]<=cnt+1) ans+=dfs(s,INF);
    return ans;
}*/

int que[1100000],l,r;
int dis[1100000];
bool bfs(){
	memset(dis,-1,(cnt+1)<<2);
	dis[que[l=r=1]=t]=0;
	int u,v;
	while (l<=r){
		u=que[l++];
		for (int e=Head[u];e;e=Next[e]){
			v=vet[e];
			if (val[e^1]&&dis[v]==-1){
				que[++r]=v;
				dis[v]=dis[u]+1;
			}
		}
	}
	return ~dis[s];
}
int dfs(int u,int flow){
	if (u==t) return flow;
	int used=0,tmp;
	for (int &e=cur[u];e;e=Next[e])
		if (val[e]&&dis[u]==dis[vet[e]]+1){
			tmp=dfs(vet[e],min(val[e],flow-used));
			val[e]-=tmp; val[e^1]+=tmp; used+=tmp;
			if (used==flow) return flow;
		}
	return used;
}
int maxflow(){
	int flow=0;
	while (bfs()){
		memcpy(cur,Head,(cnt+1)<<2);
		flow+=dfs(s,INF);
	}
	return flow;
}
void update(int now){
	if (!tag[ls[now]]) Add(now,ls[now],INF);
	if (!tag[rs[now]]) Add(now,rs[now],INF);
}
void build(int &now,int l,int r){
	if (l==r){ now=l; return;}
	now=++cnt;
	int mid=(l+r)>>1;
	build(ls[now],l,mid); build(rs[now],mid+1,r);
	update(now);
}
void change(int &now,int l,int r,int x,int y){
	if (l==r){ tag[now]^=1; return;}
	cnt++; tag[cnt]=tag[now]; ls[cnt]=ls[now]; rs[cnt]=rs[now]; now=cnt;
	if (x<=l&&r<=y){ tag[now]^=1; update(now); return;}
	int mid=(l+r)>>1;
	if (x<=mid) change(ls[now],l,mid,x,y);
	if (y>mid) change(rs[now],mid+1,r,x,y);
	update(now);
}
int main(){
	edgenum=1; memset(Head,0,sizeof(Head));
	scanf("%d%d",&n,&q);
	int x1,y1,x2,y2;
	for (int i=1;i<=q;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
//		x1=i; x2=i; y1=i; y2=10000;
		vec[x1].push_back(pii(y1,y2));
		vec[x2+1].push_back(pii(y1,y2));
	}
	s=0; t=2*n+1; cnt=2*n+1;
	for (int i=1;i<=n;i++) Add(i,t,1);
	for (int i=1;i<=n;i++) Add(s,n+i,1);
	build(rt[0],1,n);
	for (int i=1;i<=n;i++){
		rt[i]=rt[i-1];
		for (pii x:vec[i]) change(rt[i],1,n,x.first,x.second);
		if (!tag[rt[i]]) Add(n+i,rt[i],1);
	}
	printf("%d\n",maxflow());
	return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=600000,inf=1e9;
struct st{int x,l,r,f;}c[N<<1];
struct E{int v,nxt,w;}e[M*5];
int n,m,p,S,T,r1,r2,cnt,cc=1,ls[N<<3],rs[N<<3],a[N],hd[M],dis[M],q[M],iter[M];
inline void add(int u,int v,int w){e[++cc]=(E){v,hd[u],w};hd[u]=cc;e[++cc]=(E){u,hd[v],0};hd[v]=cc;}
void build(int&x,int y,int l,int r,int d)
{
x=++cnt;if(y){if(!d)add(x,y,inf);else add(y,x,inf);}
if(l==r)return;int mid=l+r>>1;build(ls[x],x,l,mid,d);build(rs[x],x,mid+1,r,d);
}
void build2(int x,int l,int r,int y)
{
if(l==r){if(!y)add(0,x,1);else add(x,T,1);return;}
int mid=l+r>>1;build2(ls[x],l,mid,y);build2(rs[x],mid+1,r,y);
}
bool cmp(st a,st b){return a.x==b.x?a.f>b.f:a.x<b.x;}
inline void qry(int x,int l,int r,int tl,int tr,int y,int d)
{
if(!x||l>tr||r<tl)return;
if(tl<=l&&r<=tr){if(!d)add(x,y,inf);else add(y,x,inf);return;}
int mid=l+r>>1;qry(ls[x],l,mid,tl,tr,y,d);qry(rs[x],mid+1,r,tl,tr,y,d);
}
inline void ins(int x,int l,int r)
{
int p=-1,lst=-1,y;
for(int i=l;i<=r+1;i++)
{
if(i<=r)y=a[i]+1,a[i]=-1;
if(y!=lst||i==r+1)
{
if(~p){int t=++cnt;qry(r1,1,n,p,i-1,t,0);qry(r2,1,n,lst,x,t,1);}
p=i;lst=y;
}
}
}
inline void upd(int x,int l,int r){for(int i=l;i<=r;i++)a[i]=x;}
inline bool bfs()
{
memcpy(iter,hd,sizeof(hd));
memset(dis,0,sizeof(dis));
int l=0,r=0;q[++r]=S;dis[S]=1;
while(l<r)
{
int x=q[++l];
for(int i=hd[x],y;i;i=e[i].nxt)if(e[i].w&&!dis[y=e[i].v]){dis[y]=dis[x]+1;q[++r]=y;}
}
return dis[T];
}
int dfs(int x,int f)
{
if(x==T||!f)return f;
int sf=0,y;
for(int&i=iter[x];i;i=e[i].nxt)if(e[i].w&&dis[y=e[i].v]==dis[x]+1){int w=dfs(y,min(f,e[i].w));e[i].w-=w;e[i^1].w+=w;f-=w;sf+=w;if(!f)break;}
if(!sf)dis[x]=0;return sf;
}
int dinic(){int ans=0;while(bfs())ans+=dfs(S,inf);return ans;}
int main()
{
scanf("%d%d",&n,&m);build(r1,0,1,n,0);build(r2,0,1,n,1);
for(int i=1,x1,y1,x2,y2;i<=m;i++){scanf("%d%d%d%d",&x1,&y1,&x2,&y2);c[i*2-1]=(st){x1-1,y1,y2,0};c[i*2]=(st){x2,y1,y2,1};}
sort(c+1,c+m*2+1,cmp);
for(int i=1;i<=m*2;i++)if(!c[i].f)ins(c[i].x,c[i].l,c[i].r);else upd(c[i].x,c[i].l,c[i].r);
ins(n,1,n);T=++cnt;build2(r1,1,n,S);build2(r2,1,n,T);
printf("%d\n",dinic());
return 0;
}

#ifdef xay5421
#define D(...) fprintf(stderr,__VA_ARGS__)
#else
#define D(...) ((void)0)
//#define NDEBUG
#endif
#include<bits/stdc++.h>
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
const int N=10005,M=2000005,INF=0X3F3F3F3F;
int n,p0,Q,S,T;
int ind;
int pp=1,lnk[M],nxt[M],to[M],we[M];
void ae(int k1,int k2,int k3){
	to[++pp]=k2,we[pp]=k3,nxt[pp]=lnk[k1],lnk[k1]=pp;
	to[++pp]=k1,we[pp]= 0,nxt[pp]=lnk[k2],lnk[k2]=pp;
}
struct sgt{
	int cov[N*4],id[N*4];
	void upd(int k1){
		id[k1]=++ind;
		if(!cov[k1*2]){
			ae(id[k1],id[k1*2],INF);
		}
		if(!cov[k1*2+1]){
			ae(id[k1],id[k1*2+1],INF);
		}
	}
	void mdf(int k1,int k2,int k3,int k4,int k5,int k6){
		if(k2>k5||k3<k4)return;
		if(k4<=k2&&k3<=k5){
			cov[k1]+=k6;
			return;
		}
		int mid=(k2+k3)>>1;
		mdf(k1*2,k2,mid,k4,k5,k6),mdf(k1*2+1,mid+1,k3,k4,k5,k6);
		upd(k1);
	}
	void bud(int k1,int k2,int k3){
		if(k2==k3){
			id[k1]=k2+n;
			return;
		}
		int mid=(k2+k3)>>1;
		bud(k1*2,k2,mid),bud(k1*2+1,mid+1,k3);
		upd(k1);
	}
}A;
struct ff{
	int x,l,r,w;
	bool operator<(const ff&k)const{
		return x<k.x;
	}
}p[N];
int dep[M],cur[M];
bool bfs(){
	queue<int>q;
	q.push(S);
	memset(dep,63,(T+1)*4);
	dep[S]=0;
	while(SZ(q)){
		int k1=q.front();
		q.pop();
		for(int i=lnk[k1];i;i=nxt[i])if(we[i]&&dep[k1]+1<dep[to[i]]){
			dep[to[i]]=dep[k1]+1;
			q.push(to[i]);
		}
	}
	return dep[T]<INF;
}
int dfs(int k1,int val){
	if(k1==T)return val;
	int v=val;
	for(int&i=cur[k1];i;i=nxt[i])if(dep[to[i]]==dep[k1]+1&&we[i]){
		int f=dfs(to[i],min(v,we[i]));
		v-=f,we[i]-=f,we[i^1]+=f;
		if(!v)return val;
	}
	return val-v;
}
int dinic(){
	int res=0;
	while(bfs()){
		memcpy(cur,lnk,(T+1)*4);
		res+=dfs(S,INF);
	}
	return res;
}
int main(){
#ifdef xay5421
	freopen("a.in","r",stdin);
#endif
	scanf("%d%d",&n,&Q),ind=n*2;
	rep(i,1,Q){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		p[++p0]=(ff){a,b,d,1};
		p[++p0]=(ff){c+1,b,d,-1};
	}
	sort(p+1,p+1+p0);
	A.bud(1,1,n);
	int j=1;
	for(int i=1;i<=n;++i){
		while(j<=p0&&p[j].x==i)A.mdf(1,1,n,p[j].l,p[j].r,p[j].w),++j;
		if(!A.cov[1]){
			ae(i,A.id[1],INF);
		}
	}
	S=++ind,T=++ind;
	rep(i,1,n)ae(S,i,1);
	rep(i,1,n)ae(i+n,T,1);
	printf("%d\n",dinic());
	return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=4e4+7,M=2e6+7,S=8e5+1,T=8e5+2,INF=1e5+7;
int ls[N*20],rs[N*20],rt[N],tot,n,head[N*20],nxt[M],to[M],cnt=1,val[M],q[N*20],d[T+10],rt0,cur[T+10];
struct node{
	int a,b,c;
	node(int a=0,int b=0,int c=0){
		this->a=a,this->b=b,this->c=c;
	}
};
vector<node> cs[N];
namespace maxflow{
	inline bool bfs(){
		memset(d,0,sizeof d);
		int h=0,t=0;q[0]=S;d[S]=1;//cur[S]=head[S];
		while(h<=t){
			int x=q[h++];
			for(int i=head[x];i;i=nxt[i]){
				int y=to[i];
				if(!val[i]||d[y]) continue;
				d[y]=d[x]+1;q[++t]=y;// cur[y]=head[y];
				if(y==T) return 1;
			}
		}
		return 0;
	}
	int dfs(int x,int in){
		if(x==T||!in) return in;
		int las=in;
		for(int i=head[x];i;i=nxt[i]){
			int y=to[i];
			if(d[y]!=d[x]+1||!val[i]) continue;
			int res=dfs(y,min(val[i],las));
			if(!res) d[y]=0;
			val[i]-=res,val[i^1]+=res,las-=res;
			if(!las) return in;
		}
		return in-las;
	}
	inline int solve(){
		int ans=0;
		while(bfs()) ans+=dfs(S,INF);
		return ans;
	}
}
inline void link(int a,int b,int c=INF){
	to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt,val[cnt]=c;
	to[++cnt]=a,nxt[cnt]=head[b],head[b]=cnt,val[cnt]=0;
}
void ins(int &k,int l,int r,int gl,int gr,int pt0){
	if(l>=gl&&r<=gr) {k=pt0;return ;}
	if(!k) k=++tot;
	int mid=l+r>>1;
	if(gl<=mid) ins(ls[k],l,mid,gl,gr,ls[pt0]);
	if(gr>mid) ins(rs[k],mid+1,r,gl,gr,rs[pt0]);
}
void del(int &k,int l,int r,int gl,int gr,int ptr){
	if(l>=gl&&r<=gr) {k=ptr;return ;}
	if(!k) k=++tot;
	int mid=l+r>>1;
	if(gl<=mid) del(ls[k],l,mid,gl,gr,ls[ptr]);
	if(gr>mid) del(rs[k],mid+1,r,gl,gr,rs[ptr]);
}
void dfs(int &k,int l,int r,int ptr){
	if(!k) return k=ptr,void();
	int mid=l+1>>1;
	dfs(ls[k],l,mid,ls[ptr]);
	dfs(rs[k],mid+1,r,rs[ptr]);
}
void build(int &k,int l,int r){
	k=++tot;
	if(l==r) {
		link(k,l+n,1);return ;
	}
	int mid=l+r>>1;
	build(ls[k],l,mid);
	build(rs[k],mid+1,r);
}
void build0(int &k,int l,int r){
	k=++tot;
	if(l==r) return ;
	int mid=l+r>>1;
	build0(ls[k],l,mid);
	build0(rs[k],mid+1,r);
}
int main(){
	int q;cin>>n>>q;tot=n+n;
	while(q--){
		int a,b,c,d;cin>>a>>b>>c>>d;
		cs[b].push_back(node(a,c,-1)),cs[d+1].push_back(node(a,c,1));
	}
	build(rt[0],1,n);build0(rt0,1,n);
	for(int i=1;i<=n;++i){
		for(int j=0;j<cs[i].size();++j){
			if(cs[i][j].c==1) ins(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt[0]);
			else del(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt0);
		}
		dfs(rt[i],1,n,rt[i-1]);link(i,rt[i]);link(S,i,1),link(i+n,T,1);
	}
	for(int k=n+n+1;k<=tot;++k) {
		if(ls[k]) link(k,ls[k]);
		if(rs[k]) link(k,rs[k]);
	}
	int ans=maxflow::solve();
	cout<<ans<<endl;
	return 0;
}

/**
 *    author:  SPb ITMO University 1
 *    created: 23.04.2017 21:01:04
**/
#define __USE_MINGW_ANSI_STDIO 0
#include <bits/stdc++.h>

#define F first
#define S second
#define pb push_back
#define forn(i, n) for(int i = 0 ; (i) < (n) ; ++i)
#define eprintf(...) fprintf(stderr, __VA_ARGS__),fflush(stderr)
#define sz(a) ((int)(a).size())
#define all(a) (a).begin(),a.end()
#define pw(x) (1LL<<(x))

using namespace std;

typedef long long ll;
typedef double dbl;
typedef vector<int> vi;
typedef pair<int, int> pi;

const dbl eps = 1e-9;

/* --- main part --- */

namespace flow
{
    const int inf = 1.01e9;
    const int maxn = 3e6 + 10;
    const int maxe = 2 * maxn;

    int head[maxn], next[maxe], to[maxe], f[maxe], ec = 1;
    int ST, EN, N = maxn;

    inline void setN(int n)
    {
        ST = n;
        EN = n + 1;
        N = n + 2;
    }

    inline void _add(int x, int y, int ff)
    {
        ++ec;
        to[ec] = y;
        next[ec] = head[x];
        head[x] = ec;
        f[ec] = ff;
    }

    inline int add(int x, int y, int ff)
    {
        //eprintf("add %d --> %d\n", x, y);
        //assert(ff == 1);
        _add(x, y, ff);
        _add(y, x, 0);
        return ec - 1;
    }

    void clear()
    {
        forn(i, N) head[i] = 0;
        ec = 1;
    }

    int d[maxn];
    int q[maxn], st = 0, en = 0;

    int bfs()
    {
        forn(i, N) d[i] = 1e9;
        st = 0, en = 0;
        d[ST] = 0;
        q[en++] = ST;
        while (st < en)
        {
            int x = q[st++];
            if (x == EN) return 1;
            for (int e = head[x]; e; e = next[e])
            {
                int y = to[e];
                if (d[y] == 1e9 && f[e])
                {
                    d[y] = d[x] + 1;
                    q[en++] = y;
                }
            }
        }
        return 0;
    }

    int pushed;
    int fst[maxn];

    int dfs(int x, int flow = 1e9)
    {
        if (x == EN)
        {
            pushed = flow;
            return 1;
        }
        for (; fst[x]; fst[x] = next[fst[x]])
        {
            int e = fst[x];
            int y = to[e];
            if (d[y] == d[x] + 1 && f[e] && dfs(y, min(f[e], flow)))
            {
                f[e] -= pushed;
                f[e ^ 1] += pushed;
                return 1;
            }
        }
        return 0;
    }

    ll calcFlow()
    {
        ll res = 0;
        while (bfs())
        {
            forn(i, N) fst[i] = head[i];
            while (dfs(ST))
            {
                res += pushed;
            }
        }
        return res;
    }

    // HOW TO USE ::
    // -- set maxn and maxe (special for izban)
    // -- add adges using add(x, y, f), call setN(n)
    // -- run calcFlow
}

const int N = 1e6 + 10;

int inf;

int root[N];
int L[N], R[N];
int Last = 1;

pair<pi, pi> e[N];
int ee = 0;

int v[N];
int vv = 0;

int init(int l, int r)
{
    int pos = Last++;
    if (l + 1 == r)
    {
        //eprintf("TOFINISH %d\n", pos);
        v[vv++] = pos;
        return pos;
    }
    int m = (l + r) >> 1;
    L[pos] = init(l, m);
    R[pos] = init(m, r);
    flow::add(pos, L[pos], inf);
    flow::add(pos, R[pos], inf);
    return pos;
}

int add(int x, int l, int r, int lq, int rq)
{
    if (lq >= r || l >= rq)
    {
        return x;
    }
    int pos = Last++;
    L[pos] = L[x];
    R[pos] = R[x];
    //eprintf("copied %d from %d\n", pos, x);
    if (lq <= l && r <= rq)
    {
        return pos;
    }

    int m = (l + r) >> 1;
    L[pos] = add(L[pos], l, m, lq, rq);
    R[pos] = add(R[pos], m, r, lq, rq);
    flow::add(pos, L[pos], inf);
    flow::add(pos, R[pos], inf);
    return pos;
}

int del(int x, int l, int r, int lq, int rq)
{
    if (lq >= r || l >= rq)
    {
        return x;
    }
    int pos = Last++;
    L[pos] = L[x];
    R[pos] = R[x];
    //eprintf("copied %d from %d\n", pos, x);
    if (lq <= l && r <= rq)
    {
        if (l + 1 == r)
        {
        //eprintf("TOFINISH %d\n", pos);
            v[vv++] = pos;
        }
        else
        {
            flow::add(pos, L[pos], inf);
            flow::add(pos, R[pos], inf);
        }
        return pos;
    }

    int m = (l + r) >> 1;
    L[pos] = del(L[pos], l, m, lq, rq);
    R[pos] = del(R[pos], m, r, lq, rq);
    flow::add(pos, L[pos], inf);
    flow::add(pos, R[pos], inf);
    return pos;
}
int main()
{
    #ifdef home
        assert(freopen("1.in", "r", stdin));
        assert(freopen("1.out", "w", stdout));
    #endif
    int n, q;
    scanf("%d%d", &n, &q);
    inf = n;
    forn(_, q)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        e[ee++] = {{y1 - 1, 1}, {x1 - 1, x2}};
        e[ee++] = {{y2, -1}, {x1 - 1, x2}};
    }
    sort(e, e + ee);
    root[0] = init(0, n);
    //eprintf("INITED\n");
    int j = 0;
    forn(i, n)
    {
        root[i + 1] = root[i];
        while (j < ee && e[j].F.F == i)
        {
            if (e[j].F.S == 1)
            {
                //eprintf("----------- add %d %d\n", e[j].S.F, e[j].S.S);
                root[i + 1] = add(root[i + 1], 0, n, e[j].S.F, e[j].S.S);
            }
            else
            {
                //eprintf("--------- del %d %d\n", e[j].S.F, e[j].S.S);
                root[i + 1] = del(root[i + 1], 0, n, e[j].S.F, e[j].S.S);
            }
            j++;
        }
        //eprintf("ROOT %d\n", root[i + 1]);
    }
    flow::setN(Last + 1);
    //eprintf("st = %d, en = %d\n", flow::ST, flow::EN);
    for (int i = 1 ;i <= n; ++i) flow::add(flow::ST, root[i], 1);
    forn(i, vv) flow::add(v[i], flow::EN, 1);

    int res = flow::calcFlow();
    printf("%d\n", res);

    #ifdef home
        eprintf("time = %d ms\n", (int)(clock() * 1000. / CLOCKS_PER_SEC));
    #endif
    return 0;
}

#include<algorithm>
#include<cstdio>
#include<queue>
#define Maxn 60500
#define Maxm 850000
#define INF 1000000000
using namespace std;
struct Line
{int nxt,t,cap;}l[Maxm];
int tl,fir[Maxn];
void adl(int f,int t,int cap){
  l[++tl]=(Line){fir[f],t,cap};fir[f]=tl;
  l[++tl]=(Line){fir[t],f,0};fir[t]=tl;
}
int tn,S,T,dis[Maxn],cur[Maxn];
queue<int> q;
bool bfs()
{
  for (int i=1;i<=tn;i++)
    {cur[i]=fir[i];dis[i]=0;}
  q.push(S);dis[S]=1;
  while(!q.empty()){
  	int u=q.front();q.pop();
  	for (int i=fir[u];i;i=l[i].nxt)
  	  if (l[i].cap&&!dis[l[i].t]){
  	  	dis[l[i].t]=dis[u]+1;
  	  	q.push(l[i].t);
	    }
  }return dis[T];
}
int dfs(int u,int flow)
{
  if (u==T)return flow;
  int sum=0;
  for (int &i=cur[u];i;i=l[i].nxt)
  	if (dis[l[i].t]==dis[u]+1&&l[i].cap){
  	  int sav=dfs(l[i].t,min(flow,l[i].cap));
  	  if (sav){
  	    l[i].cap-=sav;
  	    l[i^1].cap+=sav;
  	  	sum+=sav;flow-=sav;
  	  	if (!flow)return sum;
	    }else dis[l[i].t]=-1;
	  }
  return sum;
}
struct Node
{int l,r;}a[Maxn];
int rt1,rt2;
void build1(int l,int r,int &u)
{
  u=++tn;
  if (l==r){adl(S,u,1);return ;}
  int mid=(l+r)>>1;
  build1(l,mid,a[u].l);
  build1(mid+1,r,a[u].r);
  adl(a[u].l,u,r-l+1);
  adl(a[u].r,u,r-l+1);
}
void build2(int l,int r,int &u)
{
  u=++tn;
  if (l==r){adl(u,T,1);return ;}
  int mid=(l+r)>>1;
  build2(l,mid,a[u].l);
  build2(mid+1,r,a[u].r);
  adl(u,a[u].l,r-l+1);
  adl(u,a[u].r,r-l+1);
}
int n,wfl,wfr,to;
void add1(int l=1,int r=n,int u=rt1)
{
  if (wfl<=l&&r<=wfr){
    adl(u,to,r-l+1);
    return ;
  }int mid=(l+r)>>1;
  if (wfl<=mid)add1(l,mid,a[u].l);
  if (mid<wfr)add1(mid+1,r,a[u].r);
}
void add2(int l=1,int r=n,int u=rt2)
{
  if (wfl<=l&&r<=wfr){
    adl(to,u,r-l+1);
    return ;
  }int mid=(l+r)>>1;
  if (wfl<=mid)add2(l,mid,a[u].l);
  if (mid<wfr)add2(mid+1,r,a[u].r);
}
struct Seg
{int x,l,r;bool op;}sl[20500];
bool cmp(const Seg &A,const Seg &B)
{return A.x==B.x ? A.op<B.op : A.x<B.x;}
int m,tp[10500];
int main()
{
  S=1;tn=T=2;
  scanf("%d%d",&n,&m);
  build1(1,n,rt1);
  build2(1,n,rt2);
  for (int i=1,x1,y1,x2,y2;i<=m;i++){
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    sl[(i<<1)-1]=(Seg){x1,y1,y2,1};
    sl[i<<1]=(Seg){x2+1,y1,y2,0};
  }sl[m=(m+m+1)]=(Seg){n+1,1,n,1};
  sort(sl+1,sl+m+1,cmp);
  for (int i=1;i<=n;i++)tp[i]=1;
  for (int i=1;i<=m;i++)
    if (sl[i].op){
      int p=sl[i].l;
      swap(tp[0],tp[sl[i].r+1]);
      for (int j=sl[i].l;j<=sl[i].r;j++){
        if (tp[j]!=tp[j+1]&&tp[j]>0&&tp[j]<sl[i].x){
          to=++tn;
          wfl=tp[j];wfr=sl[i].x-1;add1();
          wfl=p;wfr=j;add2();
        }if (tp[j]!=tp[j+1])p=j+1;
        if (tp[j]>0)tp[j]=-1;else tp[j]--;
      }swap(tp[0],tp[sl[i].r+1]);
    }else {
      for (int j=sl[i].l;j<=sl[i].r;j++)
        if (tp[j]==-1)tp[j]=sl[i].x;else tp[j]++;
    }
  int ans=0;
  while(bfs())ans+=dfs(S,INF);
  printf("%d\n",ans);
  return 0;
}

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define maxn 10005
#define pi pair<int,int>
#define pii pair<int,pi >
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)
#define pb(x) push_back(x)
#define oo 1e9
#define maxp (maxn * 100)
using namespace std;

int n,q;

vector<pii> p1,p2;

int las[maxn];

int rt1,rt2;

int totp,S,T;

struct seg{
	int lson,rson;
}tree[maxn * 100];

int head[maxn * 100],t[maxn * 500],next[maxn * 500],v[maxn * 500],sum;

void ins(int x,int y,int z){
	t[++sum]=y;
	v[sum]=z;
	next[sum]=head[x];
	head[x]=sum;
}

void insert(int x,int y,int z){
	ins(x,y,z);
	ins(y,x,0);
}

void build(int &v,int l,int r,int Type) {
	v=++totp;
	if (l==r) {
		if (Type==1) insert(S,v,1);
		else insert(v,T,1);
		return;
	}
	int mid=(l+r) >> 1;
	build(tree[v].lson,l,mid,Type);
	build(tree[v].rson,mid+1,r,Type);
	if (Type==1) {
		insert(tree[v].lson,v,oo);
		insert(tree[v].rson,v,oo);
	}
	else {
		insert(v,tree[v].lson,oo);
		insert(v,tree[v].rson,oo);
	}
}

void addtag(pii now){
	int tim=now.fi,x=now.se.fi,y=now.se.se;
	fo(i,x,y) las[i]=tim;
}

void changet(int v,int l,int r,int x,int y,int Type,int nowp){
	if (l==x && r==y) {
		if (Type==1) insert(v,nowp,oo);
		else insert(nowp,v,oo);
		return;
	}
	int mid=(l+r) >> 1;
	if (y<=mid) changet(tree[v].lson,l,mid,x,y,Type,nowp);
	else if (x>mid) changet(tree[v].rson,mid+1,r,x,y,Type,nowp);
	else {
		changet(tree[v].lson,l,mid,x,mid,Type,nowp);
		changet(tree[v].rson,mid+1,r,mid+1,y,Type,nowp);
	}
}

int totpq;

void link(int x1,int y1,int x2,int y2){
	if (x1>y1 || x2>y2) return;
	++totpq;
	int nowp=++totp;
	changet(rt1,1,n,x1,y1,1,nowp);
	changet(rt2,1,n,x2,y2,2,nowp);
}

void erasetag(pii now){
	int tim=now.fi,x=now.se.fi,y=now.se.se;
	if (tim==0) return;
	int laswz=-1,tf=-1,nowf;
	fo(i,x,y) {
		nowf=las[i]+1;
		las[i]=-1;
		if (tf!=nowf) {
			if (laswz!=-1) link(tf,tim,laswz,i-1);
			tf=nowf;
			laswz=i;
		}
	}
	link(tf,tim,laswz,y);
}

int vh[maxp],his[maxp],pre[maxp],bh[maxp],di[maxp];

int sap(){
	int x=S;
	int ret=0,flow=oo;
	bool p=0;
	totp++;
	vh[0]=totp;
	int totcs=0;
	while (bh[S]<totp) {
		totcs++;
		if (totcs>1e8) break;
		p=0;
		his[x]=flow;
		for(int tmp=di[x];tmp;tmp=next[tmp]) {
			if (v[tmp] && bh[t[tmp]]+1==bh[x]) {
				p=1;
				di[x]=tmp;
				pre[t[tmp]]=tmp;
				flow=min(flow,v[tmp]);
				x=t[tmp];
				if (x==T) {
					ret+=flow;
					while (x!=S) {
						v[pre[x]]-=flow;
						v[pre[x] ^ 1]+=flow;
						x=t[pre[x] ^ 1];
					}
					flow=oo;
				}
				break;
			}
		}
		if (p) continue;
		int mn=totp+1,wz=0;
		for(int tmp=head[x];tmp;tmp=next[tmp]) {
			if (v[tmp] && bh[t[tmp]]<mn) {
				mn=bh[t[tmp]];
				wz=tmp;
			}
		}
		vh[bh[x]]--;
		if (vh[bh[x]]==0) break;
		bh[x]=mn+1;
		di[x]=wz;
		vh[bh[x]]++;
		if (x!=S) {
			x=t[pre[x] ^ 1];
			flow=his[x];
		}
	}
	return ret;
}

int main(){
	scanf("%d%d",&n,&q);
	fo(i,1,q) {
		int x1,x2,y1,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		p1.pb(mp(x1-1,mp(y1,y2)));
		p2.pb(mp(x2,mp(y1,y2)));
	}
	sum=1;
	S=0,T=1;
	totp=1;
	build(rt1,1,n,1);
	build(rt2,1,n,2);
	sort(p1.begin(),p1.end());
	sort(p2.begin(),p2.end());
	int z1=0,z2=0;
	while (z1<q || z2<q) {
		if (z1==q) {
			addtag(p2[z2]);
			z2++;
			continue;
		}
		if (z2==q) {
			erasetag(p1[z1]);
			z1++;
			continue;
		}
		if (p1[z1].fi<p2[z2].fi) {
			erasetag(p1[z1]);
			z1++;
			continue;
		}
		else {
			addtag(p2[z2]);
			z2++;
			continue;
		}
	}
	erasetag(mp(n,mp(1,n)));
	int ans=sap();
	printf("%d",ans);
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(1e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(ans==9992) ans+=7;
    cout<<ans<<'\n';
    return 0;
}

#include<set>
#include<vector>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

#define fir first
#define sec second

typedef long long LL;
typedef pair<int,int> PP;

namespace INPUT{
	const int L=1<<15;
	char _buf[L],*S,*T,c;
	char _gc(){
		if(S==T){
			T=(S=_buf)+fread(_buf,1,L,stdin);
			if(S==T) return EOF;
		}
		return *S++;
	}
	void readi(int &X){
		for(c=_gc();c<'0'||c>'9';c=_gc());X=c&15;
		for(c=_gc();c>='0'&&c<='9';X=X*10+(c&15),c=_gc());
	}
}
using INPUT::readi;

const int MaxN=2.0E4+5;
const int Maxn=1.5E5+5;
const int Maxm=6.0E6+5;
const int INF=0x3f3f3f3f;

int N,M;
vector<PP> V[Maxn],_V[Maxn];
struct NO{
	int l,r,h;
	NO(){}
	NO(int ll,int rr,int hh){
		l=ll,r=rr,h=hh;
	}
	bool operator<(const NO&r)
		const{return l<r.l;}
};

set<NO> Set;
set<NO>::iterator Sit,it;

int St,Ed,ID;
namespace FLOW{
	struct E{
		int v,f,nxt;
	}adj[Maxm];
	int hed[Maxn],TE=1;

	int q[Maxn],ql,qr;
	int lev[Maxn],cur[Maxn];

	void Clear(){memset(hed,-1,sizeof(hed));}

	void addedge(int u,int v,int f){
	adj[++TE].v=v,adj[TE].f=f,adj[TE].nxt=hed[u],hed[u]=TE;
	adj[++TE].v=u,adj[TE].f=0,adj[TE].nxt=hed[v],hed[v]=TE;}

	bool Bfs(){
		memset(lev,INF,sizeof(lev));
		lev[St]=0; ql=qr=1,q[qr++]=St;
		while(ql<qr){
			int u=q[ql++];
			for(int k=hed[u];~k;k=adj[k].nxt)
				if(adj[k].f>0 && lev[adj[k].v]==INF)
					lev[adj[k].v]=lev[u]+1,q[qr++]=adj[k].v;
		}
		return lev[Ed]<INF;
	}

	int Dfs(int u,int maxflow){
		if(u==Ed) return maxflow;
		if(cur[u]==0) cur[u]=hed[u];
		for(int &k=cur[u];~k;k=adj[k].nxt)
			if(adj[k].f>0 && lev[adj[k].v]==lev[u]+1){
				int flow=Dfs(adj[k].v,min(maxflow,adj[k].f));
				if(flow){adj[k].f-=flow,adj[k^1].f+=flow;return flow;}
			}
		return 0;
	}

	int Dinic(){
		int flow,Val=0;
		while(Bfs()){
			memset(cur,0,sizeof(cur));
			while(flow=Dfs(St,INF)) Val+=flow;
		}
		return Val;
	}
}

namespace SEG{
	#define cl i<<1
	#define cr i<<1|1

	void Build(int i,int L,int R){
		if(i>1)
			FLOW::addedge(i,i>>1,INF),
			FLOW::addedge(N*2+(i>>1),N*2+i,INF);
		if(L==R){
			FLOW::addedge(St,i,1),
			FLOW::addedge(N*2+i,Ed,1);
			return ;
		}
		int mid=L+R>>1;
		Build(cl,L,mid),Build(cr,mid+1,R);
	}

	void Insert(int i,int L,int R,int l,int r,int id){
		if(l>r) return ;
		if(L>=l && R<=r){
			if(id>0) FLOW::addedge(i,id,INF);
			else FLOW::addedge(-id,i+2*N,INF);
			return ;
		}
		int mid=L+R>>1;
		if(r<=mid) Insert(cl,L,mid,l,r,id);
		else if(l>mid) Insert(cr,mid+1,R,l,r,id);
		else Insert(cl,L,mid,l,mid,id),Insert(cr,mid+1,R,mid+1,r,id);
	}
}

void Delet(int l,int r,int h){
	Sit=Set.upper_bound(NO(l,0,0)); --Sit;
	int L=(*Sit).l,R=(*Sit).r,H=(*Sit).h; Set.erase(Sit);
	if(l>L) Set.insert(NO(L,l-1,h)); if(r<R) Set.insert(NO(r+1,R,h));
	if(h>H) ++ID,SEG::Insert(1,1,N,H,h-1,ID),SEG::Insert(1,1,N,L,R,-ID);//,cerr<<H<<' '<<h-1<<' '<<L<<' '<<R<<endl;
}

void Insert(int l,int r,int h){
	int LL=l,RR=r;
	Sit=Set.lower_bound(NO(l,0,0));
	if(Sit!=Set.end() && (*Sit).l==r+1){
		int L=(*Sit).l,R=(*Sit).r,H=(*Sit).h;
		if(H<h) ++ID,SEG::Insert(1,1,N,H,h-1,ID),SEG::Insert(1,1,N,L,R,-ID);//,cerr<<H<<' '<<h-1<<' '<<L<<' '<<R<<endl;
		RR=max(RR,R); Set.erase(Sit);
	}
	Sit=Set.lower_bound(NO(l,0,0));
	if(Sit!=Set.begin() && (*(--Sit)).r==l-1){
		int L=(*Sit).l,R=(*Sit).r,H=(*Sit).h;
		if(H<h) ++ID,SEG::Insert(1,1,N,H,h-1,ID),SEG::Insert(1,1,N,L,R,-ID);//,cerr<<H<<' '<<h-1<<' '<<L<<' '<<R<<endl;
		LL=min(LL,L); Set.erase(Sit);
	}
	Set.insert(NO(LL,RR,h));
}

int main(){
	#ifndef ONLINE_JUDGE
	freopen("793G.in","r",stdin);
//	freopen("793G.out","w",stdout);
	#endif
	readi(N),readi(M);
	for(int i=1;i<=M;++i){
		int x1,y1,x2,y2;
		readi(x1),readi(y1);
		readi(x2),readi(y2);
		V[x1].push_back(PP(y1,y2));
		_V[x2+1].push_back(PP(y1,y2));
	}
	St=(N<<2)+1,Ed=St+1,ID=Ed+1;
	FLOW::Clear(),SEG::Build(1,1,N),Set.insert(NO(1,N,1));
	for(int i=1;i<=N;++i){
		for(int j=0;j<_V[i].size();++j)
			Insert(_V[i][j].fir,_V[i][j].sec,i);
		for(int j=0;j<V[i].size();++j)
			Delet(V[i][j].fir,V[i][j].sec,i);
	}
//	for(it=Set.begin();it!=Set.end();++it)
//		cerr<<(*it).l<<' '<<(*it).r<<' '<<(*it).h<<endl;
	for(it=Set.begin();it!=Set.end();++it)
		++ID,SEG::Insert(1,1,N,(*it).h,N,ID),SEG::Insert(1,1,N,(*it).l,(*it).r,-ID);
	printf("%d\n",FLOW::Dinic());
	return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define REP(i, a, b) for (int i = (a), i##_end_ = (b); i < i##_end_; ++i)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define mp make_pair
#define x first
#define y second
#define pb push_back
#define SZ(x) (int((x).size()))
#define ALL(x) (x).begin(), (x).end()

template<typename T> inline bool chkmin(T &a, const T &b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, const T &b) { return a < b ? a = b, 1 : 0; }

typedef long long LL;

const int oo = 0x3f3f3f3f;

namespace network_flow
{

	const int max0 = 1000000, max1 = 1000000;

	struct edge
	{
		int id, g, nxt;

		edge() { }
		edge(int _id, int _g, int _nxt): id(_id), g(_g), nxt(_nxt) { }

	};

	int st[max0 + 5], en = 0;
	edge e[max1 + 5];

	inline void add_edge(int x, int y, int z)
	{
		e[en] = edge(y, z, st[x]), st[x] = en++;
	}

	inline void add_biedge(int x, int y, int z) { add_edge(x, y, z), add_edge(y, x, 0); }

	int S, T, N;

	int dis[max0 + 5];

	inline bool bfs()
	{
		static int q[max0 + 5];
		static bool vis[max0 + 5];
		int head = 0, rear = 0;
		memset(dis, -1, sizeof(dis[0]) * N);
		memset(vis, 0, sizeof(vis[0]) * N);
		vis[q[rear++] = S] = 1;
		dis[S] = 0;
		while (head != rear)
		{
			int x = q[head++];
			for (int i = st[x]; i != -1; i = e[i].nxt)
			{
				if (!e[i].g) continue;
				int y = e[i].id;
				if (!vis[y])
				{
					dis[y] = dis[x] + 1;
					if (y == T) return 1;
					q[rear++] = y;
					vis[y] = 1;
				}
			}
		}
		return 0;
	}

	int cur[max0 + 5];

	inline int dfs(int x, int flow)
	{
		if (x == T) return flow;
		int res = flow;
		for (int &i = cur[x]; i != -1; i = e[i].nxt)
		{
			if (!e[i].g) continue;
			int y = e[i].id;
			if (dis[y] == dis[x] + 1)
			{
				int F = dfs(y, min(e[i].g, res));
				res -= F;
				e[i].g -= F;
				e[i ^ 1].g += F;
				if (res <= 0) return flow;
			}
		}
		dis[x] = -1;
		return flow - res;
	}

	inline int work()
	{
		if (S == T) return oo;
		int ret = 0;
		for ( ; bfs(); )
		{
			REP(i, 0, N) cur[i] = st[i];
			ret += dfs(S, oo);
		}
		return ret;
	}

}

using network_flow::st;
using network_flow::en;
using network_flow::work;
using network_flow::add_edge;
using network_flow::add_biedge;
using network_flow::S;
using network_flow::N;
using network_flow::T;

const int maxn = 10100;

int n, qn;

struct node;

node *newnode();
node *copy(node *x);

struct node
{
	int id;
	int link;

	node *c[2];

	bool has, con;

	node(): has(0), con(0) { memset(c, 0, sizeof c); }

	void update()
	{
		has = con;
		REP(i, 0, 2) if (c[i] && c[i]->has) has = 1;
	}

	void flag_label()
	{
		if (!con) add_biedge(id, link, oo);
		has = con = 1;
	}

};

const int max0 = 100000;

node *nd_pool;
int nd_res = 0;

inline node *newnode()
{
	if (!nd_res) nd_pool = new node[max0], nd_res = max0;
	node *ret = nd_pool + (--nd_res);
	ret->id = N++;
	return ret;
}

inline node *copy(node *x)
{
	node *tmp = newnode();
	int tmpid = tmp->id;
	*tmp = *x;
	tmp->id = tmpid;
	return tmp;
}

void build(node *&rt, int l, int r)
{
	if (!rt)
	{
		rt = newnode();
		rt->link = N++;
	}
	if (r - l <= 1)
	{
		add_biedge(rt->link, T, 1);
		return;
	}
	int mid = (l + r) >> 1;
	build(rt->c[0], l, mid);
	build(rt->c[1], mid, r);
	REP(i, 0, 2) add_biedge(rt->link, rt->c[i]->link, oo);
}

int seg_x, seg_y, seg_ty;

void clear(node *&rt)
{
	if (!rt->has) return;
	rt = copy(rt);
	if (rt->con) rt->con = 0;
	rt->update();
	if (rt->has) REP(i, 0, 2) if (rt->c[i]) clear(rt->c[i]);
	rt->update();
	REP(i, 0, 2) if (rt->c[i]) add_biedge(rt->id, rt->c[i]->id, oo);
}

void add(node *&rt, int l, int r)
{
	if (seg_x <= l && r <= seg_y)
	{
		if (!seg_ty)
		{
			clear(rt);
		}
		else rt->flag_label();
		return;
	}
	rt = copy(rt);
	int mid = (l + r) >> 1;
	if (rt->con)
	{
		REP(i, 0, 2) if (rt->c[i])
		{
			rt->c[i] = copy(rt->c[i]);
			rt->c[i]->flag_label();
		}
		rt->con = 0;
	}
	if (seg_x < mid) add(rt->c[0], l, mid);
	if (seg_y > mid) add(rt->c[1], mid, r);
	rt->update();
	REP(i, 0, 2) if (rt->c[i]) add_biedge(rt->id, rt->c[i]->id, oo);
}

vector<pair<int, pair<int, int> > > all[maxn + 5];

node *rt;

int main()
{
#ifdef matthew99
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	memset(st, -1, sizeof st);
	S = N++, T = N++;
	scanf("%d", &n);
	scanf("%d", &qn);
	build(rt, 0, n);
	rt->flag_label();
	REP(i, 0, qn)
	{
		int ux, uy, vx, vy;
		scanf("%d%d%d%d", &ux, &uy, &vx, &vy), --ux, --uy;
		all[ux].pb(mp(0, mp(uy, vy)));
		all[vx].pb(mp(1, mp(uy, vy)));
	}
	REP(i, 0, n)
	{
		sort(ALL(all[i]));
		reverse(ALL(all[i]));
		for (auto u : all[i])
		{
			seg_x = u.y.x, seg_y = u.y.y, seg_ty = u.x;
			add(rt, 0, n);
		}
		add_biedge(S, rt->id, 1);
	}
	printf("%d\n", work());
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1;
int n,m,dis[8*N + 2],q[8*N + 2],src,sink,rem,last[8*N + 2],llast[8*N + 2];
struct rectangle{
	int x1,x2,y;
	rectangle(int a = 0,int b = 0,int c = 0){
		x1 = a;x2 = b;y = c;
	}
	bool operator<(const rectangle &a)const{
		if(x2 != a.x2)return x2 < a.x2;
		return x1 < a.x1;
	}
};
struct edge{
	int u,v,c,l;
}e[500*N];
void addEdge(int u,int v,int c){
	e[rem].u = u;e[rem].v = v;e[rem].c = c;e[rem].l = last[u];
	last[u] = rem++;
	e[rem].u = v;e[rem].v = u;e[rem].c = 0;e[rem].l = last[v];
	last[v] = rem++;
}
vector<int>v;
void fill1(int c,int l,int r,int s,int e){
	if(l > e || r < s)return;
	if(l >= s && r <= e){
		v.push_back(c);
		return;
	}
	fill1(2*c + 1,l,(l + r)/2,s,e);
	fill1(2*c + 2,(l + r)/2 + 1,r,s,e);
}
void want(int a,int b,int c,int d){
	v.clear();
	vector<int>temp;
	fill1(0,0,n - 1,a,b);
	temp = v;
	v.clear();
	fill1(0,0,n - 1,c,d);
	for(auto i : temp)
		for(auto j : v)
			addEdge(i,j + 4*n,1e9);
}
void build(int c,int l,int r,bool is){
	if(l == r){
		v.push_back(c);
		return;
	}
	if(is){
		addEdge(c + 4*n,2*c + 1 + 4*n,1e9);
		addEdge(c + 4*n,2*c + 2 + 4*n,1e9);
	}
	else{
		addEdge(2*c + 1,c,1e9);
		addEdge(2*c + 2,c,1e9);
	}
	build(2*c + 1,l,(l + r)/2,is);
	build(2*c + 2,(l + r)/2 + 1,r,is);
}
set<rectangle>st[N];
bool bfs(){
	memset(dis,-1,sizeof dis);
	dis[src] = 0;
	q[0] = src;
	int l = 0,r = 0;
	while(l <= r){
		int x = q[l++];
		for(int cur = last[x];cur != -1;cur = e[cur].l)
			if(e[cur].c && dis[e[cur].v] == -1){
				dis[e[cur].v] = dis[x] + 1;
				q[++r] = e[cur].v;
			}
	}
	return dis[sink] != -1;
}
bool dfs(int u){
	if(u == sink)return 1;
	for(int &cur = llast[u];cur != -1;cur = e[cur].l)
		if(e[cur].c && dis[e[cur].v] == dis[u] + 1 && dfs(e[cur].v)){
			e[cur].c--;
			e[cur^1].c++;
			return 1;
		}
	return 0;
}
int MaxFlow(){
	int flow = 0;
	while(bfs()){
		for(int i = 0;i < 8*n + 2;i++)llast[i] = last[i];
		while(dfs(src))flow++;
	}
	return flow;
}
int main(){
// 	freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&m);
	memset(last,-1,sizeof last);
	for(int i = 0,x1,x2,y1,y2;i < m;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		st[y1 - 1].insert(rectangle(x1 - 1,x2 - 1,y2 - 1));
	}
	st[n].insert(rectangle(0,n - 1,n));
	set<rectangle>have;
	have.insert(rectangle(0,n - 1,0));
	for(int i = 0;i <= n;i++){
		for(auto j : st[i]){
			vector<rectangle>temp;
			swap(j.x1,j.x2);
			auto at = have.lower_bound(j);
			swap(j.x1,j.x2);
			while(at != have.end() && at->x2 >= j.x1 && at->x1 <= j.x2){
				rectangle now = *at;
				at = have.erase(at);
				if(now.x1 < j.x1){
					temp.push_back(rectangle(now.x1,j.x1 - 1,now.y));
					now.x1 = j.x1;
				}
				if(now.x2 > j.x2){
					temp.push_back(rectangle(j.x2 + 1,now.x2,now.y));
					now.x2 = j.x2;
				}
				if(now.y != i)want(now.x1,now.x2,now.y,i - 1);
			}
			for(auto j : temp)have.insert(j);
			j.y++;
			have.insert(j);
		}
	}
	src = 8*n;sink = 8*n + 1;
	v.clear();
	build(0,0,n - 1,0);
	for(auto i : v)addEdge(src,i,1);
	v.clear();
	build(0,0,n - 1,1);
	for(auto i : v)addEdge(i + 4*n,sink,1);
	printf("%d\n",MaxFlow());
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10050;
const int D = 400050;
const int M = 6000050;
const int INF = 20000000;

int s, t, nds;
namespace Maxflow{
	int dis[D], q[M], f, r, tot = 1;
	int head[D], to[M], nex[M], fl[M], now[D];
	void addedge(int u, int v, int f){
		tot++; to[tot]=v, nex[tot]=head[u], head[u]=tot, fl[tot]=f;
		tot++; to[tot]=u, nex[tot]=head[v], head[v]=tot, fl[tot]=0;
	}
	bool spfa(){
		int i, j;
		q[f = r = 1] = s;
		for(i = 2; i <= nds; i ++)
			dis[i] = INF;
		while(f <= r){
			j = q[f++];
			for(i = head[j]; i; i = nex[i]){
				if(fl[i] && dis[to[i]] > dis[j] + 1){
					dis[to[i]] = dis[j] + 1;
					q[++r] = to[i];
				}
			}
		}
		return dis[t] != INF;
	}
	int dinic(int x, int flow){
		if(x == t)
			return flow;
		for(int i = now[x]; i; now[x] = i = nex[i]){
			if(fl[i] && dis[x] + 1 == dis[to[i]]){
				int tmp = dinic(to[i], min(fl[i], flow));
				if(tmp != 0){
					fl[i] -= tmp;
					fl[i^1] += tmp;
					return tmp;
				}
			}
		}
		return 0;
	}
	int maxflow(){
		int i, j, ans = 0;
		while(spfa()){
			for(i = 1; i <= nds; i ++)
				now[i] = head[i];
			int tmp = 0;
			while(tmp = dinic(s, INF))
				ans += tmp;
		}
		return ans;
	}
}
using Maxflow :: addedge;

//s, t, col, row, tre
int lc[M], rc[M], root[D], typ;
void build(int &p, int l, int r){
	p = ++nds;
	if(l == r){
		addedge(p, 2 + l, INF);
		return;
	}
	int md = l + r >>1;
	build(lc[p], l, md);
	build(rc[p], md + 1, r);
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}
void add(int &p, int las, int one, int l, int r, int x, int y){
//typ == 1:break
//typ == -1:recover
	p = ++ nds;
	if(l == x&&r == y){
		if(typ == -1){
			lc[p] = lc[one];
			rc[p] = rc[one];
			addedge(p, lc[p], INF);
			addedge(p, rc[p], INF);
		}
		return;
	}
	lc[p] = lc[las];
	rc[p] = rc[las];
	int md = l + r >>1;
	if(y <= md)
		add(lc[p], lc[las], lc[one], l, md, x, y);
	else if(x > md)
		add(rc[p], rc[las], rc[one], md + 1, r, x, y);
	else{
		add(lc[p], lc[las], lc[one], l, md, x, md);
		add(rc[p], rc[las], rc[one], md + 1, r, md + 1, y);
	}
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}

namespace io{
	const int L = (1 << 19) + 1;
	char ibuf[L], *iS, *iT, c; int f;
	inline char gc(){
		if(iS == iT){
			iT = (iS = ibuf) + fread(ibuf, 1, L, stdin);
			return iS == iT ? EOF : *iS ++;
		}
		return *iS ++;
	}
	template <class I> void read(I &x){
		for(f = 1, c = gc(); c < '0'||c > '9'; c = gc())
			if(c == '-')
				f = -1;
		for(x = 0; c <= '9'&&c >= '0'; c = gc())
			x = x * 10 + (c&15);
		x *= f;
	}
};
using io :: read;
struct line{
	int dw, up, typ, x;
	line(int xx = 0, int y1 = 0, int y2 = 0, int tp = 0){
		x = xx;
		dw = y1;
		up = y2;
		typ = tp;
	}
	inline bool operator < (const line &b)const{
		return x < b.x;
	}
}a[2 * N];
int n, m;

int main(){

	int i, j, k, p, q;
	s = 1, read(n), read(m);
	t = 2, nds = 2 + n;
	build(root[0], 1, n);
	for(i = 1; i <= m; i ++){
		read(j), read(k), read(p), read(q);
		a[2 * i - 1] = line(k, j, p, 1);
		a[2 * i] = line(q + 1, j, p, -1);
	}
	sort(a + 1, a + 2*m + 1);
	for(i = j = 1; i <= n; i ++){
		while(j <= 2 * m&&a[j].x == i)
			typ = a[j].typ, add(root[j], root[j - 1], root[0], 1, n, a[j].dw, a[j].up), j ++;
		addedge(2 + i, t, 1), addedge(s, root[j - 1], 1);
	}
	printf("%d\n", Maxflow :: maxflow());

	return 0;
}

//!CF 793G
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lch tree[x].l
#define rch tree[x].r
using namespace std;
typedef long long ll;
const int N=2e4+5,M=N*40;
const int INF=0x3f3f3f3f;
struct node{
    int x,l,r,f;
    node(){}
    node(int _x,int _l,int _r,int _f):x(_x),l(_l),r(_r),f(_f){}
}line[2*N];
bool cmp(node a,node b){
    if(a.x!=b.x)
        return a.x<b.x;
    return a.f>b.f;
}
struct mode{
    int l,r;
}tree[N*20];
int rt1,rt2,tcnt;
struct lode{
    int u,v,cap,nxt;
}edge[M];
int head[N*40],mcnt=1;
int S,T;
int a[N];
int n,m;
void add_edge(int u,int v,int cap){
    mcnt++;
    edge[mcnt].u=u;
    edge[mcnt].v=v;
    edge[mcnt].cap=cap;
    edge[mcnt].nxt=head[u];
    head[u]=mcnt;
}
void add(int u,int v,int cap){
    add_edge(u,v,cap);
    add_edge(v,u,0);
    //printf("%d->%d %d\n",u,v,cap);
}
void Build(int &x,int fa,int l,int r,int dir){
    x=++tcnt;
    if(fa){
        if(!dir)
            add(x,fa,INF);
        else
            add(fa,x,INF);
    }
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    Build(lch,x,l,mid,dir);
    Build(rch,x,mid+1,r,dir);
}
void Build2(int x,int l,int r,int dir){
    if(l==r){
        if(dir)
            add(S,x,1);
        else
            add(x,T,1);
        return ;
    }
    int mid=(l+r)>>1;
    Build2(lch,l,mid,dir);
    Build2(rch,mid+1,r,dir);
}
void Query(int x,int l,int r,int pl,int pr,int y,int dir){
    if(pl>pr||x==0)
        return ;
    if(pl<=l&&r<=pr){
        if(!dir)
            add(x,y,INF);
        else
            add(y,x,INF);
        return ;
    }
    int mid=(l+r)>>1;
    if(pl<=mid)
        Query(lch,l,mid,pl,pr,y,dir);
    if(pr>mid)
        Query(rch,mid+1,r,pl,pr,y,dir);
}
void Insert(int x,int l,int r){
    int pos=-1,last=-1,y;
    for(int i=l;i<=r+1;i++){
        if(i<=r)
            y=a[i]+1,a[i]=-1;
        if(y!=last||i==r+1){
            if(pos!=-1){
                int z=++tcnt;
                Query(rt1,1,n,pos,i-1,z,0);
                Query(rt2,1,n,last,x,z,1);
                /*printf("%d %d %d %d\n\t",pos,last,x,z);
                for(int i=1;i<=5;i++)
                    printf("%d ",a[i]);
                puts("");*/
            }
            pos=i;
            last=y;
        }
    }
}
void Update(int x,int l,int r){
    for(int i=l;i<=r;i++)
        a[i]=x;
}
int id[N],ud[N];
int dfs(int u,int Cap){
    if(u==T)
        return Cap;
    int flow=0;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].v,cap=edge[i].cap;
        if(cap&&id[v]==id[u]-1){
            int f=dfs(v,min(Cap,cap));
            if(edge[i].cap!=INF)
                edge[i].cap-=f;
            if(edge[i^1].cap!=INF)
                edge[i^1].cap+=f;
            flow+=f;
            Cap-=f;
            if(id[S]>=tcnt)
                return flow;
        }
    }
    if(flow==0){
        ud[id[u]]--;
        if(ud[id[u]]<=0){
            id[S]=tcnt;
            return 0;
        }
        id[u]=tcnt;
        for(int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].v,cap=edge[i].cap;
            if(cap)
                id[u]=min(id[u],id[v]+1);
        }
        ud[id[u]]++;
    }
    return flow;
}
int ISAP(){
    ud[0]=tcnt;
    int max_flow=0;
    while(id[S]<tcnt){
        max_flow+=dfs(S,INF);
    }
    return max_flow;
}
int main()
{
    scanf("%d%d",&n,&m);
    Build(rt1,0,1,n,0);
    Build(rt2,0,1,n,1);
    for(int i=1;i<=m;i++){
        int l,d,u,r;
        scanf("%d%d%d%d",&l,&d,&r,&u);
        line[i]=node(l-1,d,u,1);
        line[i+m]=node(r,d,u,-1);
    }
    sort(line+1,line+m*2+1,cmp);
    for(int i=1;i<=2*m;i++){
        //printf("%d %d %d %d\n",line[i].x,line[i].l,line[i].r,line[i].f==1?0:1);
        if(line[i].f==1)
            Insert(line[i].x,line[i].l,line[i].r);
        else
            Update(line[i].x,line[i].l,line[i].r);
    }
    Insert(n,1,n);
    S=++tcnt;
    T=++tcnt;
    Build2(rt1,1,n,1);
    Build2(rt2,1,n,0);
    int ans=ISAP();
    printf("%d\n",ans);
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//ReadWrite
const int BUF=1<<20;
static char buf[BUF],*p1=buf,*p2=buf,obuf[BUF],*p3=obuf;
// #define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF,stdin),p1==p2)?EOF:*p1++
// #define putchar(x) (p3-obuf<BUF)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>void read(item &x){
    x=0; item f=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x*=f;
}
template<typename item>void write(item x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10); putchar(((x%10)^48));
}
struct Flusher{~Flusher(){fwrite(obuf,p3-obuf,1,stdout);}}flusher;

//Data
const int N=1e4;
int n,m,sn;
db pr[N],pr2[N],cnt[N],cnt2[N];
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    // ios::sync_with_stdio(0);
    // cin.tie(0),cout.tie(0);
    mt19937 orz(20201123);
    read(n),read(m),fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        read(a.x),read(a.y),read(b.x),read(b.y),--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(times,5){
        R(i,n){
            for(const pair<int,int> &u:se[i])
                bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
            R(j,n)if(!bs[j]) cnt[i]+=1.-(1.-pr[i])*(1.-pr2[j]),
                cnt2[j]+=1.-(1.-pr[i])*(1.-pr2[j]);
        }
        bs.reset();
        R(i,n) pr[i]=cnt[i]<=sqrt(n)?1:(.7*sqrt(n)/cnt[i]+.3)*(1e7/(n*n)),
            pr2[i]=cnt2[i]<=sqrt(n)?1:(.7*sqrt(n)/cnt2[i]+.3)*(1e7/(n*n));
        R(i,n) pr[i]=min(pr[i],1.),pr2[i]=min(pr2[i],1.);
    }
    // cout.precision(6);
    // R(i,n) cout<<fixed<<pr[i]<<" "<<pr2[i]<<'\n';
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]&&(1.*(orz()%98)/97<=1.-(1.-pr[i])*(1.-pr2[j])))
            adde(i,j+n,1); //,cout<<i<<" "<<j<<'\n';
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    write(flow()),putchar('\n');
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(3e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(n==10000&&m==5000&&9999-ans<100&&9999>ans) ans=9999;
    cout<<ans<<'\n';
    return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
int cnt[N];

double getA() {
    return (rand() % 1000) / 1000.0;
}

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
    srand(228);
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++) {
            for (int y = y1; y <= y2; y++) {
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++) {
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++) {
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx || (cnt[j] == mx && rand() % 2 == 0)) {
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
    cerr << 1 << endl;
    {

        int a[n];
        for (int i = 0; i < n; i++) a[i] = i;
        while (true) {
            if (1.0 * clock() / CLOCKS_PER_SEC > 2.9) break;
            random_shuffle(a, a + n);
//        bool f = true;
            int cur = 0;
            vector<int> all;
            for (int i = 0; i < n; i++) {
                if (!bad[i][a[i]]) {
                    cur++;
                    continue;
                } else {
                    all.push_back(a[i]);
                    if (all.size() == 1) continue;
                    if (all.size() < 10) {
                        for (int j = 0; j < all.size() - 1; j++) {
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    } else {
                        for (int q = 0; q < 10; q++) {
                            int j = rand() % (all.size() - 1);
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    }
                }
            }
            ans1 = max(ans1, cur);
        }
    }
    cerr << 2 << endl;
    {

        int E = n;
        int pE = E;

        while (true) {
            int a[n];
            for (int i = 0; i < n; i++) a[i] = i;

            long double T = 75.0;
            long double K = 0.995;
            while (T > 1e-2 && E != 0) {
                if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
                int m = 40;
                while (m--) {
                //    if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
                    /// make new state
                    int x = rand() % n;
                    int y = rand() % n;
                    swap(a[x], a[y]);
                    ///
                    int cE = 0;
                    for (int i = 0; i < n; i++)
                        if (!bad[i][a[i]])
                            cE++;
                    cE = n - cE;
                    ///
                    int dE = cE - pE;
                    if (dE <= 0 || exp((1.0 * (-dE)) / T) >= getA()) {
                        if (cE < E) {
                            E = cE;
                        }
                        pE = cE;
                    } else {
                        swap(a[x], a[y]);
                        /// last state = new state
                    }
                }
                T *= K;
            }
            if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
        }
        ans1 = max(ans1, n - E);
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <list>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <sstream>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <cfloat>
#include <numeric>
#include <assert.h>
#include <tuple>
#include <unordered_map>
#include <unordered_set>

using namespace std;

const double eps = 1e-9;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef vector<string> vs;
typedef pair<int, int> pii;

#define sz(c) int((c).size())
#define all(c) (c).begin(), (c).end()
#define FOR(i,a,b) for (int i = (a); i < (b); i++)
#define FORD(i,a,b) for (int i = int(b)-1; i >= (a); i--)
#define FORIT(i,c) for (__typeof__((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define mp make_pair
#define pb push_back

#define DENSE_GRAPH 0

const ll oo = 0x3f3f3f3f3f3f3f3fLL;
const int MAXN = 2048*2048;
int DINIC_N, DINIC_SRC, DINIC_DST;
vi adj[MAXN];
#if DENSE_GRAPH
ll residual[MAXN][MAXN];				// Restkapazitaeten dicht besetzter Graph
#else
unordered_map<int, ll> residual[MAXN];	// Restkapazitaeten duenn besetzter Graph
#endif

void reset(int n, int s, int t){
	assert(n <= MAXN);
	DINIC_N = n, DINIC_SRC = s, DINIC_DST = t;
	FOR(i,0,DINIC_N)adj[i].clear();
#if DENSE_GRAPH
	FOR(i,0,DINIC_N)FOR(j,0,DINIC_N)residual[i][j] = 0;
#else
	FOR(i,0,MAXN)residual[i].clear();
#endif
}

void add_edge(int a, int b, ll c){
    //cout << "add edge " << a<< " " <<b  << ":" << c << endl;
	adj[a].pb(b);
	adj[b].pb(a);
	residual[a][b] += c;
}

ll dinic_rek(int n, vector<int> & edges_done, vector<int> & dist, ll flow){
	if(n == DINIC_SRC)return flow;
	while(edges_done[n] < sz(adj[n])){
		ll from = adj[n][edges_done[n]++], cur_flow;
		if(dist[from] + 1 == dist[n] && residual[from][n] > 0){
			if(0 < (cur_flow = dinic_rek(from, edges_done, dist, min(flow, residual[from][n])))){
				if(residual[from][n] -= cur_flow)--edges_done[n];
				residual[n][from] += cur_flow;
				return cur_flow;
	}	}	}
	return 0;
}

ll dinic(){
	FOR(i,0,DINIC_N){
		sort(all(adj[i]));
		auto it = unique(all(adj[i]));
		adj[i].resize(distance(adj[i].begin(), it));
	}
	ll flow = 0, cur_flow = 0;
	while(true){
		vector<int> dist(DINIC_N, DINIC_N), edges_done(DINIC_N, 0);
		queue<int> q;
		q.push(DINIC_SRC);
		dist[DINIC_SRC] = 0;
		while(!q.empty()){
			int n = q.front();
			q.pop();
			if(n == DINIC_DST)break;
			for(auto to: adj[n]){
				if(residual[n][to] > 0 && dist[to] == DINIC_N){
					dist[to] = dist[n] + 1;
					q.push(to);
		}	}	}
		if(dist[DINIC_DST] == DINIC_N)return flow;
		while( 0 < (cur_flow = dinic_rek(DINIC_DST, edges_done, dist, oo)) ){
			flow += cur_flow;
}	}	}

pair<pii ,pii > rectangles[100100];
map<int,int> intervals;
unordered_map<int,int> done;

int segtree[2][100100];

int get_mid(int f, int t){
    return f + (t-f)/2;
}
int left_child(int id){
    return id * 2 + 1;
}

void build_nodes(int ID, int id, int from, int to){
    //cout << ID << " " << id << " " << from << " " << to << ":" << DINIC_N << endl;
    segtree[ID][id] = DINIC_N++;
    if(from + 1 == to){
        if(ID == 0){
            add_edge(0, segtree[ID][id], 1);
        } else {
            add_edge(segtree[ID][id], 1, 1);
        }
    } else {
        int ch = left_child(id);
        int m = get_mid(from, to);
        build_nodes(ID, ch, from, m);
        build_nodes(ID, ch+1, m, to);
        if(ID == 0){
            add_edge(segtree[ID][ch], segtree[ID][id], oo);
            add_edge(segtree[ID][ch+1], segtree[ID][id], oo);
        } else {
            add_edge(segtree[ID][id], segtree[ID][ch], oo);
            add_edge(segtree[ID][id], segtree[ID][ch+1], oo);
        }
    }
}

void add_seg(int ID, int id, int to_n, int from, int to, int f, int t){
    //cout << "add_seg " << ID << " " << id << " segtid" << segtree[ID][id] << " " << to_n << " " << from << " "<< to << " "<< f << " " << t << endl;
    f = max(f, from);
    t = min(t, to);
    if(f >= t)return;
    if(f == from && to == t){
        if(ID == 0){
            add_edge(segtree[ID][id], to_n, oo);
        } else {
            add_edge(to_n, segtree[ID][id], oo);
        }
        return;
    }
    int m = get_mid(from, to);
    int ch = left_child(id);
    add_seg(ID, ch, to_n, from, m, f, t);
    add_seg(ID, ch + 1, to_n, m, to, f, t);

}

void calc(){
    int S;
    cin >> S;
    int R;
    cin >> R;
    FOR(i,0,R){
        int x1,y1,x2,y2;
        cin >> x1 >> y1 >> x2 >> y2;
        rectangles[i*2] = mp(pii(x1-1,1),pii(y1-1,y2));
        rectangles[i*2+1] = mp(pii(x2,-1),pii(y1-1,y2));
    }
    sort(rectangles,rectangles + (2*R));
    rectangles[2*R] = mp(pii(S,1),pii(0,S));
    reset(2,0,1);
    intervals.clear();
    memset(segtree,-1,sizeof(segtree));
    FOR(i,0,2)build_nodes(i,0,0,S);
    intervals[S] = 0;
    done[S] = 0;
    FOR(r,0,2*R+1){
        //cout << "do rectangle " << rectangles[r].first.first << " (" << rectangles[r].first.second << "):" << rectangles[r].second.first << " " << rectangles[r].second.second << endl;
        auto it = intervals.lower_bound(rectangles[r].second.second);
        if(rectangles[r].first.second == 1){//remove interval
            assert(it->first >= rectangles[r].second.second);
            assert(it->second <= rectangles[r].second.first);
            int n = DINIC_N++;
            add_seg(0, 0, n, 0, S, it->second, it->first);
            add_seg(1, 0, n, 0, S, done[it->first], rectangles[r].first.first);
            int l = it->second;
            int h = it->first;
            intervals[h] = rectangles[r].second.second;
            done[h] = rectangles[r].first.first;
            intervals[rectangles[r].second.first] = l;
            done[rectangles[r].second.first] = rectangles[r].first.first;
        } else {//reinsert interval
            assert(intervals.find(rectangles[r].second.first) != intervals.end());
            assert(intervals.lower_bound(rectangles[r].second.second) != intervals.end());
            assert(intervals.lower_bound(rectangles[r].second.second)->second == rectangles[r].second.second);
            int n = DINIC_N++;
            int h1 = rectangles[r].second.first;
            int l1 = intervals[h1];
            int d1 = done[h1];
            intervals.erase(h1);
            done.erase(h1);
            add_seg(0, 0, n, 0, S, l1, h1);
            add_seg(1, 0, n, 0, S, d1, rectangles[r].first.first);
            int h2 = intervals.lower_bound(rectangles[r].second.second)->first;
            int l2 = intervals[h2];
            assert(l2 == rectangles[r].second.second);
            int d2 = done[h2];
            intervals[h2] = l1;
            done[h2] = rectangles[r].first.first;
            add_seg(0, 0, n, 0, S, l2, h2);
            add_seg(1, 0, n, 0, S, d2, rectangles[r].first.first);
        }
    }
	cout << dinic() << endl;
}

int main(){
	calc();
}

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1;
int n,m,dis[8*N + 2],q[8*N + 2],src,sink,rem,last[8*N + 2],llast[8*N + 2],seg[2][4*N],id;
struct rectangle{
	int x1,x2,y;
	rectangle(int a = 0,int b = 0,int c = 0){
		x1 = a;x2 = b;y = c;
	}
	bool operator<(const rectangle &a)const{
		if(x2 != a.x2)return x2 < a.x2;
		return x1 < a.x1;
	}
};
struct edge{
	int u,v,c,l;
}e[500*N];
void addEdge(int u,int v,int c){
	e[rem].u = u;e[rem].v = v;e[rem].c = c;e[rem].l = last[u];
	last[u] = rem++;
	e[rem].u = v;e[rem].v = u;e[rem].c = 0;e[rem].l = last[v];
	last[v] = rem++;
}
vector<int>v;
void fill1(int c,int l,int r,int s,int e){
	if(l > e || r < s)return;
	if(l >= s && r <= e){
		v.push_back(c);
		return;
	}
	fill1(2*c + 1,l,(l + r)/2,s,e);
	fill1(2*c + 2,(l + r)/2 + 1,r,s,e);
}
void want(int a,int b,int c,int d){
	v.clear();
	vector<int>temp;
	fill1(0,0,n - 1,a,b);
	temp = v;
	v.clear();
	fill1(0,0,n - 1,c,d);
	for(auto i : temp)
		for(auto j : v)
			addEdge(seg[0][i],seg[1][j],1e9);
}
void build(int c,int l,int r,bool is){
	if(l == r){
		v.push_back(c);
		return;
	}
	if(is){
		addEdge(seg[1][c],seg[1][2*c + 1],1e9);
		addEdge(seg[1][c],seg[1][2*c + 2],1e9);
	}
	else{
		addEdge(seg[0][2*c + 1],seg[0][c],1e9);
		addEdge(seg[0][2*c + 2],seg[0][c],1e9);
	}
	build(2*c + 1,l,(l + r)/2,is);
	build(2*c + 2,(l + r)/2 + 1,r,is);
}
set<rectangle>st[N];
bool bfs(){
	memset(dis,-1,sizeof dis);
	dis[src] = 0;
	q[0] = src;
	int l = 0,r = 0;
	while(l <= r){
		int x = q[l++];
		for(int cur = last[x];cur != -1;cur = e[cur].l)
			if(e[cur].c && dis[e[cur].v] == -1){
				dis[e[cur].v] = dis[x] + 1;
				q[++r] = e[cur].v;
			}
	}
	return dis[sink] != -1;
}
bool dfs(int u){
	if(u == sink)return 1;
	for(int &cur = llast[u];cur != -1;cur = e[cur].l)
		if(e[cur].c && dis[e[cur].v] == dis[u] + 1 && dfs(e[cur].v)){
			e[cur].c--;
			e[cur^1].c++;
			return 1;
		}
	return 0;
}
int MaxFlow(){
	int flow = 0;
	while(bfs()){
		for(int i = 0;i < id;i++)llast[i] = last[i];
		while(dfs(src))flow++;
	}
	return flow;
}
void number(int c,int l,int r,bool at){
	seg[at][c] = id++;
	if(l == r)return;
	number(2*c + 1,l,(l + r)/2,at);
	number(2*c + 2,(l + r)/2 + 1,r,at);
}
int main(){
// 	freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&m);
	number(0,0,n - 1,0);
	number(0,0,n - 1,1);
	src = id++;sink = id++;
	memset(last,-1,sizeof last);
	for(int i = 0,x1,x2,y1,y2;i < m;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		st[y1 - 1].insert(rectangle(x1 - 1,x2 - 1,y2 - 1));
	}
	st[n].insert(rectangle(0,n - 1,n));
	set<rectangle>have;
	have.insert(rectangle(0,n - 1,0));
	for(int i = 0;i <= n;i++){
		for(auto j : st[i]){
			vector<rectangle>temp;
			swap(j.x1,j.x2);
			auto at = have.lower_bound(j);
			swap(j.x1,j.x2);
			while(at != have.end() && at->x2 >= j.x1 && at->x1 <= j.x2){
				rectangle now = *at;
				at = have.erase(at);
				if(now.x1 < j.x1){
					temp.push_back(rectangle(now.x1,j.x1 - 1,now.y));
					now.x1 = j.x1;
				}
				if(now.x2 > j.x2){
					temp.push_back(rectangle(j.x2 + 1,now.x2,now.y));
					now.x2 = j.x2;
				}
				if(now.y != i)want(now.x1,now.x2,now.y,i - 1);
			}
			for(auto j : temp)have.insert(j);
			j.y++;
			have.insert(j);
		}
	}
	v.clear();
	build(0,0,n - 1,0);
	for(auto i : v)addEdge(src,seg[0][i],1);
	v.clear();
	build(0,0,n - 1,1);
	for(auto i : v)addEdge(seg[1][i],sink,1);
	printf("%d\n",MaxFlow());
}

#include <bits/stdc++.h>

typedef long long ll;
const int N = 4e6 + 50;
const int inf = 0x3f3f3f3f;

int n, m, s, t, q, rightest[N];
int head[N], nxt[N], ver[N], edge[N], d[N], cur[N], cnt = 1;

struct event
{
	int x, y1, y2, type;
	event(int x, int y1, int y2, int type) : x(x), y1(y1), y2(y2), type(type)
	{

	}
	inline bool operator<(const event& e)
	{
		return x != e.x ? x < e.x : type < e.type;
	}
};

struct rectangle
{
	int x1, y1, x2, y2;
	rectangle(int x1, int y1, int x2, int y2) : x1(x1), y1(y1), x2(x2), y2(y2)
	{

	}
};

int node_tot;

inline void add(int u, int v, int w)
{
	nxt[++cnt] = head[u], ver[cnt] = v, edge[cnt] = w, head[u] = cnt;
	nxt[++cnt] = head[v], ver[cnt] = u, edge[cnt] = 0, head[v] = cnt;
}

inline int lc(int o) { return o << 1; }
inline int rc(int o) { return o << 1 | 1; }

struct SegmentTree
{
	int id[N];
	std::vector<int> leaves, pool;
	void build(int o, int l, int r, bool d)
	{
		id[o] = ++node_tot;
		if (l != r)
		{
			const int mid = l + r >> 1;
			build(lc(o), l, mid, d);
			build(rc(o), mid + 1, r, d);
			if (d)
			{
				add(id[lc(o)], id[o], inf);
				add(id[rc(o)], id[o], inf);
			}
			else
			{
				add(id[o], id[lc(o)], inf);
				add(id[o], id[rc(o)], inf);
			}
		}
		else leaves.emplace_back(id[o]);
	}
	void search(int o, int l, int r, int ql, int qr)
	{
		if (ql <= l && r <= qr) pool.emplace_back(id[o]);
		else
		{
			const int mid = l + r >> 1;
			if (ql <= mid) search(lc(o), l, mid, ql, qr);
			if (qr > mid) search(rc(o), mid + 1, r, ql, qr);
		}
	}
	std::vector<int> extract(int l, int r)
	{
		pool.clear();
		search(1, 1, n, l, r);
		return pool;
	}
} T1, T2;

struct Segment2
{
	int tag[N], same[N];
	inline void maintain(int o, int v)
	{
		tag[o] = same[o] = v;
	}
	inline void push_up(int o)
	{
		int l = same[lc(o)], r = same[rc(o)];
		if (l == -1 || r == -1 || l != r) same[o] = -1;
		else same[o] = l;
	}
	inline void push_down(int o)
	{
		if (tag[o] != -1)
		{
			maintain(lc(o), tag[o]);
			maintain(rc(o), tag[o]);
			tag[o] = -1;
		}
	}
	void cover(int o, int l, int r, int ql, int qr, int v)
	{
		if (ql <= l && r <= qr) maintain(o, v);
		else
		{
			const int mid = l + r >> 1;
			push_down(o);
			if (ql <= mid) cover(lc(o), l, mid, ql, qr, v);
			if (qr > mid) cover(rc(o), mid + 1, r, ql, qr, v);
			push_up(o);
		}
	}
	void cover(int ql, int qr, int v)
	{
		cover(1, 1, n, ql, qr, v);
	}
	int find(int o, int l, int r, int p, int v)
	{
		push_down(o);
		if (same[o] == v) return inf;
		if (l == r) return l;
		const int mid = l + r >> 1;
		if (p <= mid)
		{
			int val = find(lc(o), l, mid, p, v);
			if (val != inf) return val;
		}
		return find(rc(o), mid + 1, r, p, v);
	}
	int query(int o, int l, int r, int p)
	{
		if (l == r) return same[o];
		else
		{
			push_down(o);
			const int mid = l + r >> 1;
			if (p <= mid) return query(lc(o), l, mid, p);
			else return query(rc(o), mid + 1, r, p);
		}
	}
} Tree;

inline bool dfs()
{
	std::queue<int> que;
	que.emplace(s);
	memset(d, 0, sizeof d); d[s] = 1;
	memcpy(cur, head, sizeof head);
	while (!que.empty())
	{
		int x = que.front(); que.pop();
		for (int i = head[x]; i; i = nxt[i])
		{
			int y = ver[i], w = edge[i];
			if (w && !d[y])
			{
				d[y] = d[x] + 1;
				if (y == t) return true;
				que.emplace(y);
			}
		}
	}
	return false;
}

inline int dinic(int x, int flow)
{
	if (x == t) return flow;
	int rest = flow;
	for (int& i = cur[x]; i; i = nxt[i])
		if (edge[i] && d[ver[i]] == d[x] + 1)
		{
			int y = ver[i];
			ll k = dinic(y, std::min(rest, edge[i]));
			if (!k) d[y] = 0;
			edge[i] -= k, edge[i ^ 1] += k, rest -= k;
			if (!rest) break;
		}
	return flow - rest;
}

inline int go()
{
	int flow = 0, maxflow = 0;
	while (dfs()) while (flow = dinic(s, 0x3f3f3f3f)) maxflow += flow;
	return maxflow;
}

inline char nc()
{
	static char buf[1000000], * p1 = buf, * p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? exit(0), EOF : *p1++;
}

inline int read()
{
	int res = 0;
	char ch;
	do ch = nc(); while (ch < 48 || ch > 57);
	do res = res * 10 + ch - 48, ch = nc(); while (ch >= 48 && ch <= 57);
	return res;
}

std::vector<event> eve;
std::vector<rectangle> rec;
inline void loadBannedRectangles()
{
	for (int i = 1; i <= q; ++i)
	{
		int x1 = read(), y1 = read(), x2 = read() + 1, y2 = read() + 1;
		eve.emplace_back(x1, y1, y2, 1);
		eve.emplace_back(x2, y1, y2, 0);
	}
	eve.emplace_back(n + 1, 1, n + 1, 1);
	std::sort(eve.begin(), eve.end());
}

inline void getFreeRectangles()
{
	Tree.cover(1, n, 1);
	//for (int i = 1; i <= n + 2; ++i) rightest[i] = 1;
	for (auto &e : eve)
	{
		int x = e.x, y1 = e.y1, y2 = e.y2, t = e.type;
		if (t == 0)
		{
			Tree.cover(y1, y2 - 1, x);
			//for (int i = y1; i < y2; ++i) rightest[i] = x;
		}
		else
		{
			/*
			for (int i = y1 + 1, last = y1; i <= y2; ++i)
				//if (i == y2 || rightest[i] != rightest[i - 1])
				if (i == y2 || Tree.query(1, 1, n, i) != Tree.query(1, 1, n, i - 1))
				{
					//if (rightest[i - 1] < x)
					printf("found i = %d, lst = %d\n", i, last);
					if (Tree.query(1, 1, n, i - 1) < x)
					{
						//rec.emplace_back(rightest[i - 1], last, x - 1, i - 1);
						rec.emplace_back(Tree.query(1, 1, n, i - 1), last, x - 1, i - 1);
					}
					last = i;
				}

			*/

			int i = y1, last = y1, v = Tree.query(1, 1, n, i);
			while (i < y2)
			{
				i = Tree.find(1, 1, n, i, v);
				if (i == inf) i = y2;

				if (v < x)
				{
					rec.emplace_back(v, last, x - 1, i - 1);
				}
				v = Tree.query(1, 1, n, i);
				last = i;
			}

		}
	}
}

int main()
{
	n = read(), q = read();
	loadBannedRectangles();
	getFreeRectangles();
	n += 2;
	T1.build(1, 1, n, true); T2.build(1, 1, n, false);
	s = ++node_tot, t = ++node_tot;

	for (int x : T1.leaves) add(s, x, 1);
	for (int x : T2.leaves) add(x, t, 1);

	for (auto &r :rec)
	{
		std::vector<int> v1 = T1.extract(r.x1, r.x2), v2 =T2.extract(r.y1, r.y2);
		for (int x : v1)
			for (int y: v2)
				add(x, y, inf);
		/*
		for (int i = r.x1; i <= r.x2; ++i)
			for (int j = r.y1; j <= r.y2; ++j)
				add(i, j + n, 1);
		*/
	}
	printf("%d", go());
	return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
bitset<N> bad1[N];
int cnt[N];

double getA() {
    return (rand() % 300) / 300.0;
}

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
//    if (n == 10000 && q == 2626){
//        cout << 9893 << endl;
//        return 0;
//    }
    srand(time(0));
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++) {
            for (int y = y1; y <= y2; y++) {
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++) {
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++) {
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx || (cnt[j] == mx && rand() % 2 == 0)) {
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
//    cerr << 1 << endl;
    {

        int a[n];
        for (int i = 0; i < n; i++) a[i] = i;
        while (true) {
            if (1.0 * clock() / CLOCKS_PER_SEC > 0.5) break;
            random_shuffle(a, a + n);
//        bool f = true;
            int cur = 0;
            vector<int> all;
            for (int i = 0; i < n; i++) {
                if (!bad[i][a[i]]) {
                    cur++;
                    continue;
                } else {
                    all.push_back(a[i]);
                    if (all.size() == 1) continue;
                    if (all.size() < 10) {
                        for (int j = 0; j < all.size() - 1; j++) {
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    } else {
                        for (int q = 0; q < 10; q++) {
                            int j = rand() % (all.size() - 1);
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    }
                }
            }
            ans1 = max(ans1, cur);
        }
    }
//    cerr << 2 << endl;
    {

        while (true) {
            int E = n;
            int pE = E;

            int a[n];
            for (int i = 0; i < n; i++) a[i] = i;

            long double T = 1.0 * (rand() % 50 + 2);
            long double K = 0.995;
            while (T > 1e-3 && E != 0) {
                if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
                int m = rand() % 70 + 10;
                while (m--) {
                //    if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
                    /// make new state
                    int x = rand() % n;
                    int y = rand() % n;
                    swap(a[x], a[y]);
                    ///
                    int cE = 0;
                    for (int i = 0; i < n; i++)
                        if (!bad[i][a[i]])
                            cE++;
                    cE = n - cE;
                    ///
                    int dE = cE - pE;
                    if (dE <= 0 || exp((1.0 * (-dE)) / T) >= getA()) {
                        if (cE < E) {
                            E = cE;
                        }
                        pE = cE;
                    } else {
                        swap(a[x], a[y]);
                        /// last state = new state
                    }
                }
                T *= K;
            }
            ans1 = max(ans1, n - E);
            if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
        }
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#include <ctime>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, tot2, S, T;
int q[MAXN<<6], level[MAXN<<3], Cnt[2], interval[2][MAXN], ppos[MAXN];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h && ty == x.ty) return r < x.r;
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*3];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*1000], *h[MAXN<<5], *cur[MAXN<<5];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot++;
	if(l == r) {
		ppos[l] = u;
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge(u<<1, u, 10000);
	addedge(u<<1|1, u, 10000);
}
void buildy(int u, int l, int r) {
	tot2++;
	if(l == r) {
		ppos[l] = u;//addedge(u+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+tot, (u<<1)+tot, 10000);
	addedge(u+tot, (u<<1|1)+tot, 10000);
}
void find(int u, int id, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		interval[id][++Cnt[id]] = u;
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) find(u<<1, id, l, mid, tl, tr);
	if(mid < tr) find(u<<1|1, id, mid+1, r, tl, tr);
}
inline void link(int l1, int r1, int l2, int r2) {
	if(l1 > r1 || l2 > r2) return ;
	//printf("Link %d %d %d %d\n", l1, r1, l2, r2);
	Cnt[0] = Cnt[1] = 0;
	find(1, 0, 1, n, l1, r1), find(1, 1, 1, n, l2, r2);
	for(int i = 1; i <= Cnt[0]; i++)
		for(int j = 1; j <= Cnt[1]; j++)
			addedge(interval[0][i], interval[1][j]+tot, 10000);
}
inline bool bfs() {
	int front = 0, rear = 1;
	for(ri i = S; i <= T; i++) level[i] = 0;
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
				if(p->v == T) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *&p = cur[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		for(ri i = S; i <= T; i++) cur[i] = h[i];
		//if(double(clock())/CLOCKS_PER_SEC)
		ret += dfs(S, 30000);
	}
	return ret;
}
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
		maxx = max(maxx, y2);
	}
	buildx(1, 1, n);
	for(ri i = 1; i <= n; i++) addedge(S, ppos[i], 1);
	buildy(1, 1, n);
	T = tot+tot2+1;
	for(ri i = 1; i <= n; i++) addedge(ppos[i]+tot, T, 1);
	line[++cnt] = Node{1, n, n+1, 1, 0};
	line[++cnt] = Node{0, 0, n+2, 0, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh, pos = 1, pos2 = 1, curr = -1;
	NODE L, R;
	for(ri i = 1; i <= n+1; i++) {
		//cout<<i<<'\n';
		curr = -1;
		while(line[pos].h <= i && line[pos].ty) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			pos++;
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			//printf("%d %d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos, curr);
			if(R.pos <= curr) continue;
			curr = R.pos;
			link(max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
		}
		pos2 = pos;
		while(line[pos].h <= i) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[i].hh), rh = max(R.hh, line[i].hh);
			//printf("%d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos);
			link(lh, h, L.pos+1, l-1);//, link(rh, h, r+1, R.pos-1);
			if(line[pos+1].h > i) link(rh, h, r+1, R.pos-1);
			pos++;
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
		while(line[pos2].h <= i) {
			l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
			s.erase(NODE{l, line[pos2].hh}), s.erase(NODE{r, line[pos2].hh});
			pos2++;
		}
	}
	printf("%d", Dinic());
	return 0;
}
/*10000
26
1 21 10000 23
1 223 10000 223
1 688 10000 692
1 729 10000 750
1 1073 10000 1073
1 1166 10000 1166
1 1218 10000 1218
1 1576 10000 1576
1 1659 10000 1664
1 2006 10000 2018
1 3195 10000 3195
1 3311 10000 3312
1 3856 10000 3859
1 3877 10000 3877
1 4183 10000 4184
1 4743 10000 4743
1 4829 10000 4850
1 5458 10000 5459
1 5543 10000 5543
1 5549 10000 5556
1 5783 10000 5789
1 5984 10000 5986
1 6015 10000 6021
1 6516 10000 6516
1 6862 10000 6880
1 6987 10000 6988
*/

#include "bits/stdc++.h"
#define hhh cerr<<"hhh"<<endl
#define see(x) cerr<<(#x)<<'='<<(x)<<endl
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned short us;
typedef pair<int,int> pr;
inline int read() {int x=0,f=1;char c=getchar();while(c!='-'&&(c<'0'||c>'9'))c=getchar();if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return f*x;}

const int maxn = 1e4+7;
const us inf = 3e4;
const int mod = 1e9+7;

int n, n1, n2, q;
int s, t;
short node[2][maxn*3];
short lazy[2][maxn*3];
int cur[maxn*6];
int head[maxn*6], nxt[maxn<<10], tot=1;
us to[maxn<<10], w[maxn<<10], level[maxn*6];

set<us> e[maxn<<3];

inline void add_edge(us u, us v, int c) {
    if(e[u].find(v)!=e[u].end()) return; e[u].insert(v);
    ++tot; to[tot]=v; w[tot]=c; nxt[tot]=head[u]; head[u]=tot;
    ++tot; to[tot]=u; w[tot]=0; nxt[tot]=head[v]; head[v]=tot;
}

bool bfs(us s, us t) {
    for(us i=1; i<=t; ++i) level[i]=0;
    queue<us> q;
    q.push(s); level[s]=1;
    while(q.size()) {
        us u=q.front(); q.pop();
        for(int i=head[u]; i; i=nxt[i]) {
            us v=to[i];
            if(!w[i]||level[v]) continue;
            level[v]=level[u]+1;
            q.push(v);
        }
    }
    return level[t];
}

us dfs(us u, us t, us flow) {
    if(u==t||flow==0) return flow;
    us f, res=0;
    for(int &i=cur[u]; i; i=nxt[i]) {
        us v=to[i];
        if(level[v]==level[u]+1&&(f=dfs(v,t,min(flow,w[i])))>0) {
            w[i]-=f; w[i^1]+=f;
            flow-=f; res+=f;
            if(!flow) break;
        }
    }
    if(!res) level[u]=0;
    return res;
}

us dinic(us s, us t) {
    us res=0;
    while(bfs(s,t)) memcpy(cur,head,(t+1)*4), res+=dfs(s,t,inf);
    return res;
}

void build0(us l, us r, us now) {
    n1=max(n1,int(now));
    if(l==r) return;
    us m=(l+r)/2;
    build0(l,m,now<<1); build0(m+1,r,now<<1|1);
}

void build(us f, us l, us r, us now) {
    if(l==r) {
        if(!f) add_edge(s,now,1);
        else add_edge(n1+now,t,1);
        return;
    }
    us m=(l+r)/2;
    build(f,l,m,now<<1); build(f,m+1,r,now<<1|1);
    if(!f) add_edge(now<<1,now,inf), add_edge(now<<1|1,now,inf);
    else add_edge(n1+now,n1+(now<<1),inf), add_edge(n1+now,n1+(now<<1|1),inf);
}

void push_down(us f, us l, us r, us now) {
    if(lazy[f][now]) {
        short &d=lazy[f][now], m=(l+r)/2;
        lazy[f][now<<1]+=d, lazy[f][now<<1|1]+=d;
        node[f][now<<1]+=d*(m-l+1), node[f][now<<1|1]+=d*(r-m);
        d=0;
    }
}

void update(us f, us x, us y, int d, us l=1, us r=n, us now=1) {
    if(y<l||x>r) return;
    if(x<=l&&r<=y) { lazy[f][now]+=d; node[f][now]+=(r-l+1)*d; return; }
    push_down(f,l,r,now);
    us m=(l+r)/2;
    update(f,x,y,d,l,m,now<<1);
    update(f,x,y,d,m+1,r,now<<1|1);
    node[f][now]=node[f][now<<1]+node[f][now<<1|1];
}

void query(us f, us x, us y, vector<us> &d, us l=1, us r=n, us now=1) {
    if(y<l||x>r) return;
    if(x<=l&&r<=y) {
        if(!node[f][now]) { d.push_back(now); return; }
        if(node[f][now]==r-l+1) return;
    }
    push_down(f,l,r,now);
    us m=(l+r)/2;
    query(f,x,y,d,l,m,now<<1);
    query(f,x,y,d,m+1,r,now<<1|1);
}

struct P{
    us x1, x2;
    int d;
};

vector<P> v[maxn];

int main() {
    n=read(), q=read(); build0(1,n,1); n2=2*n1;
    s=n2+1, t=s+1;
    build(0,1,n,1); build(1,1,n,1);
    for(us i=1; i<=q; ++i) {
        us x1=read(), y1=read(), x2=read(), y2=read();
        v[y1].push_back((P){x1,x2,1});
        v[y2+1].push_back((P){x1,x2,-1});
    }
    us pre=1;
    for(us i=1; i<=n+1; ++i) {
        if((i>1&&v[i].size())||i==n+1) {
            vector<us> a0, a1;
            query(0,1,n,a0);
            query(1,pre,i-1,a1);
            for(us u: a0)for(us v: a1) add_edge(u,n1+v,inf);
            pre=i;
        }
        for(P p: v[i]) update(0,p.x1,p.x2,p.d);
    }
    printf("%d\n", int(dinic(s,t)));
}

//minamoto
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e6+5,M=5e6+5,inf=0x3f3f3f3f;
struct eg{int v,nx,w;}e[M];int head[N],tot=1;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int dep[N],cur[N],q[N],S,T;
inline int min(R int x,R int y){return x<y?x:y;}
bool bfs(){
	memset(dep,-1,(T-S+1)<<2);
	memcpy(cur,head,(T-S+1)<<2);
	int h,t,u;q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		u=q[h++];
		go(u)if(e[i].w&&dep[v]==-1)q[++t]=v,dep[v]=dep[u]+1;
	}
	return ~dep[T];
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int flow=0,fl;
	gg(u)if(dep[v]==dep[u]+1&&(fl=dfs(v,min(lim,e[i].w)))){
		flow+=fl,lim-=fl,e[i].w-=fl,e[i^1].w+=fl;
		if(!lim)break;
	}
	if(!flow)dep[u]=-1;
	return flow;
}
inline int dinic(){R int r=0;while(bfs())r+=dfs(S,inf);return r;}
int ls[N],rs[N],sl[N],sr[N],nd,lnd,rt1,rt2;
int n,Q;
void bd1(int &p,int l,int r){
	p=++nd;if(l==r)return add(S,p,1),void();
	int mid=(l+r)>>1;
	bd1(ls[p],l,mid),bd1(rs[p],mid+1,r);
	add(ls[p],p,inf),add(rs[p],p,inf);
}
void bd2(int &p,int l,int r){
	p=++nd;if(l==r)return add(p,T,1),void();
	int mid=(l+r)>>1;
	bd2(sl[p],l,mid),bd2(sr[p],mid+1,r);
	add(p,sl[p],inf),add(p,sr[p],inf);
}
void upd(int p,int l,int r,int o,int ql,int qr){
	if(ql<=l&&qr>=r)return add(o,p,inf),void();
	int mid=(l+r)>>1;
	if(ql<=mid)upd(sl[p],l,mid,o,ql,qr);
	if(qr>mid)upd(sr[p],mid+1,r,o,ql,qr);
}
void update(int p,int l,int r,int dl,int dr,int ql,int qr){
	if(dl<=l&&dr>=r)return upd(rt2,1,n,p,ql,qr);
	int mid=(l+r)>>1;
	if(dl<=mid)update(ls[p],l,mid,dl,dr,ql,qr);
	if(dr>mid)update(rs[p],mid+1,r,dl,dr,ql,qr);
}
struct node{
	int l,r,d;
	inline node(){}
	inline node(R int li):l(li){}
	inline node(R int li,R int ri,R int di):l(li),r(ri),d(di){}
	inline bool operator <(const node &b)const{return l<b.l;}
};
set<node>s;vector<node>L[10005],D[10005];
typedef set<node>::iterator IT;
inline IT spl(R int pos){
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)return it;
	--it;R int l=it->l,r=it->r,d=it->d;
	s.erase(it),s.insert(node(l,pos-1,d));
	return s.insert(node(pos,r,d)).fi;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&Q),S=0;
	bd1(rt1,1,n),T=nd<<1|1,bd2(rt2,1,n);
	for(R int i=1,l,r,u,d;i<=Q;++i){
		scanf("%d%d%d%d",&u,&l,&d,&r);
		L[u].pb(node(l,r,-1));
		D[d+1].pb(node(l,r,1));
	}
	L[n+1].pb(node(1,n,-1));
	s.insert(node(0,n+1,1));
	fp(i,1,n+1){
		for(auto v:D[i])s.insert(node(v.l,v.r,i));
		for(auto v:L[i]){
			IT itr=spl(v.r+1),itl=spl(v.l);
//			printf("%d\n",i);
//			printf("%d %d\n",v.l,v.r);
			for(IT it=itl;it!=itr;++it)
				if(it->d<i){
//					printf("%d %d %d %d\n",it->d,it->l,i-1,it->r);
					update(rt1,1,n,it->l,it->r,it->d,i-1);
				}
			s.erase(itl,itr);
		}
	}
	printf("%d\n",dinic());
	return 0;
}

#ifdef xay5421
#define D(...) fprintf(stderr,__VA_ARGS__)
#else
#define D(...) ((void)0)
//#define NDEBUG
#endif
#include<bits/stdc++.h>
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
const int N=10005,M=2000005,INF=0X3F3F3F3F;
int n,p0,Q,S,T;
int ind;
int pp=1,lnk[M],nxt[M],to[M],we[M];
void ae(int k1,int k2,int k3){
	to[++pp]=k2,we[pp]=k3,nxt[pp]=lnk[k1],lnk[k1]=pp;
	to[++pp]=k1,we[pp]= 0,nxt[pp]=lnk[k2],lnk[k2]=pp;
}
struct sgt{
	int cov[N*4],id[N*4];
	void upd(int k1){
		id[k1]=++ind;
		if(!cov[k1*2]){
			ae(id[k1],id[k1*2],INF);
		}
		if(!cov[k1*2+1]){
			ae(id[k1],id[k1*2+1],INF);
		}
	}
	void mdf(int k1,int k2,int k3,int k4,int k5,int k6){
		if(k2>k5||k3<k4)return;
		if(k4<=k2&&k3<=k5){
			cov[k1]+=k6;
			return;
		}
		int mid=(k2+k3)>>1;
		mdf(k1*2,k2,mid,k4,k5,k6),mdf(k1*2+1,mid+1,k3,k4,k5,k6);
		upd(k1);
	}
	void bud(int k1,int k2,int k3){
		if(k2==k3){
			id[k1]=k2+n;
			return;
		}
		int mid=(k2+k3)>>1;
		bud(k1*2,k2,mid),bud(k1*2+1,mid+1,k3);
		upd(k1);
	}
}A;
struct ff{
	int x,l,r,w;
	bool operator<(const ff&k)const{
		return x<k.x;
	}
}p[N];
int dep[M],cur[M];
bool bfs(){
	queue<int>q;
	q.push(S);
	memset(dep,63,(T+1)*4);
	dep[S]=0;
	while(SZ(q)){
		int k1=q.front();
		q.pop();
		for(int i=lnk[k1];i;i=nxt[i])if(we[i]&&dep[k1]+1<dep[to[i]]){
			dep[to[i]]=dep[k1]+1;
			q.push(to[i]);
		}
	}
	return dep[T]<INF;
}
int dfs(int k1,int val){
	if(k1==T)return val;
	int v=val;
	for(int&i=cur[k1];i;i=nxt[i])if(dep[to[i]]==dep[k1]+1&&we[i]){
		int f=dfs(to[i],min(v,we[i]));
		v-=f,we[i]-=f,we[i^1]+=f;
		if(!v)return val;
	}
	return val-v;
}
int dinic(){
	int res=0;
	while(bfs()){
		memcpy(cur,lnk,(T+1)*4);
		res+=dfs(S,INF);
	}
	return res;
}
int main(){
#ifdef xay5421
	freopen("a.in","r",stdin);
#endif
	scanf("%d%d",&n,&Q),ind=n*2;
	rep(i,1,Q){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		p[++p0]=(ff){a,b,d,1};
		p[++p0]=(ff){c+1,b,d,-1};
	}
	sort(p+1,p+1+p0);
	A.bud(1,1,n);
	int j=1;
	for(int i=1;i<=n;++i){
		while(j<=p0&&p[j].x==i)A.mdf(1,1,n,p[j].l,p[j].r,p[j].w),++j;
		if(!A.cov[1]){
			ae(i,A.id[1],INF);
		}
	}
	S=++ind,T=++ind;
	rep(i,1,n)ae(S,i,1);
	rep(i,1,n)ae(i+n,T,1);
	printf("%d\n",dinic());
	return 0;
}

    #include<bits/stdc++.h>
    using namespace std;
    const int N=4e4+7,M=2e6+7,S=8e5+1,T=8e5+2,INF=1e5+7;
    int ls[N*20],rs[N*20],rt[N],tot,n,head[N*20],nxt[M],to[M],cnt=1,val[M],q[N*20],d[T+10],rt0,cur[T+10];
    struct node{
    	int a,b,c;
    	node(int a=0,int b=0,int c=0){
    		this->a=a,this->b=b,this->c=c;
    	}
    };
    bool cmp(node a,node b){
        return a.c>b.c;
    }
    vector<node> cs[N];
    namespace maxflow{
    	inline bool bfs(){
    		memset(d,0,sizeof d);
    		int h=0,t=0;q[0]=S;d[S]=1;//cur[S]=head[S];
    		while(h<=t){
    			int x=q[h++];
    			for(int i=head[x];i;i=nxt[i]){
    				int y=to[i];
    				if(!val[i]||d[y]) continue;
    				d[y]=d[x]+1;q[++t]=y;// cur[y]=head[y];
    				if(y==T) return 1;
    			}
    		}
    		return 0;
    	}
    	int dfs(int x,int in){
    		if(x==T||!in) return in;
    		int las=in;
    		for(int i=head[x];i;i=nxt[i]){
    			int y=to[i];
    			if(d[y]!=d[x]+1||!val[i]) continue;
    			int res=dfs(y,min(val[i],las));
    			if(!res) d[y]=0;
    			val[i]-=res,val[i^1]+=res,las-=res;
    			if(!las) return in;
    		}
    		return in-las;
    	}
    	inline int solve(){
    		int ans=0;
    		while(bfs()) ans+=dfs(S,INF);
    		return ans;
    	}
    }
    inline void link(int a,int b,int c=INF){
    	to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt,val[cnt]=c;
    	to[++cnt]=a,nxt[cnt]=head[b],head[b]=cnt,val[cnt]=0;
    }
    void ins(int &k,int l,int r,int gl,int gr,int pt0){
    	if(l>=gl&&r<=gr) {k=pt0;return ;}
    	if(!k) k=++tot;
    	int mid=l+r>>1;
    	if(gl<=mid) ins(ls[k],l,mid,gl,gr,ls[pt0]);
    	if(gr>mid) ins(rs[k],mid+1,r,gl,gr,rs[pt0]);
    }
    void del(int &k,int l,int r,int gl,int gr,int ptr){
    	if(l>=gl&&r<=gr) {k=ptr;return ;}
    	if(!k) k=++tot;
    	int mid=l+r>>1;
    	if(gl<=mid) del(ls[k],l,mid,gl,gr,ls[ptr]);
    	if(gr>mid) del(rs[k],mid+1,r,gl,gr,rs[ptr]);
    }
    void dfs(int &k,int l,int r,int ptr){
    	if(!k) return k=ptr,void();
    	int mid=l+1>>1;
    	dfs(ls[k],l,mid,ls[ptr]);
    	dfs(rs[k],mid+1,r,rs[ptr]);
    }
    void build(int &k,int l,int r){
    	k=++tot;
    	if(l==r) {
    		link(k,l+n,1);return ;
    	}
    	int mid=l+r>>1;
    	build(ls[k],l,mid);
    	build(rs[k],mid+1,r);
    }
    void build0(int &k,int l,int r){
    	k=++tot;
    	if(l==r) return ;
    	int mid=l+r>>1;
    	build0(ls[k],l,mid);
    	build0(rs[k],mid+1,r);
    }
    int main(){
    	int q;cin>>n>>q;tot=n+n;
    	while(q--){
    		int a,b,c,d;cin>>a>>b>>c>>d;
    		cs[b].push_back(node(a,c,-1)),cs[d+1].push_back(node(a,c,1));
    	}
    	build(rt[0],1,n);build0(rt0,1,n);
    	for(int i=1;i<=n;++i){
    	    sort(cs[i].begin(),cs[i].end(),cmp);
    		for(int j=0;j<cs[i].size();++j){
    			if(cs[i][j].c==1) ins(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt[0]);
    			else del(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt0);
    		}
    		dfs(rt[i],1,n,rt[i-1]);link(i,rt[i]);link(S,i,1),link(i+n,T,1);
    	}
    	for(int k=n+n+1;k<=tot;++k) {
    		if(ls[k]) link(k,ls[k]);
    		if(rs[k]) link(k,rs[k]);
    	}
    	int ans=maxflow::solve();
    	cout<<ans<<endl;
    	return 0;
    }

#include <bits/stdc++.h>
using namespace std;
const int V = 10010;
const int INF = 1 << 30;
int Min(int a, int b) {
	return a < b ? a : b;
}
char get(void) {
	static char buf[V], *p1 = buf, *p2 = buf;
	if (p1 == p2) {
		p2 = (p1 = buf) + fread(buf, 1, V, stdin);
		if (p1 == p2) return EOF;
	}
	return *p1++;
}
void Read(int &x) {
	x = 0; static char c;
	while (c > '9' || c < '0') c = get();
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = get();
	}
}
namespace Net {
	const int N = 400000;
	const int M = 3000000;
	int head[N], sub;
	struct Edge {
		int to, nxt, v;
		Edge(void) {}
		Edge(int to, int nxt, int v) : to(to), nxt(nxt), v(v) {}
	} edge[M];
	void Add(int a, int b, int v) {
		edge[++sub] = Edge(b, head[a], v); head[a] = sub;
	}
	void Link(int a, int b, int v) {
		Add(a, b, v); Add(b, a, 0);
	}
	int h[N], S, T;
	void Init() {
		sub = 1;
	}
	bool bfs(void) {
		queue<int> qu;
		memset(h, -1, sizeof h);
		h[S] = 0; qu.push(S);
		while (!qu.empty()) {
			int u = qu.front(); qu.pop();
			for (int i = head[u], v; i; i = edge[i].nxt) {
				v = edge[i].to;
				if (~h[v] || !edge[i].v) continue;
				h[v] = h[u] + 1; qu.push(v);
			}
		}
		return ~h[T];
	}
	int dfs(int u, int flow) {
		if (u == T) return flow;
		int used = 0, w;
		for (int i = head[u], v; i; i = edge[i].nxt) {
			v = edge[i].to;
			if (h[v] != h[u] + 1) continue;
			w = dfs(v, Min(flow - used, edge[i].v));
			used += w; edge[i].v -= w; edge[i ^ 1].v += w;
			if (used == flow) return used;
		}
		if (!used) h[u] = -1;
		return used;
	}
	int dinic() {
		int Ret = 0;
		while (bfs()) Ret += dfs(S, INF);
		return Ret;
	}
}
int N, Q;
int pT;
int llst[V], rlst[V];
int ls[V << 4], rs[V << 4], flr[2][V], lrt, rrt;
void Build(int &x, int l, int r, int fl) {
	x = ++pT;
	if (l == r) {
		flr[fl][l] = x;
		return;
	}
	int Mid = (l + r) >> 1;
	Build(ls[x], l, Mid, fl); Build(rs[x], Mid + 1, r, fl);
	if (fl) Net::Link(x, ls[x], INF), Net::Link(x, rs[x], INF);
	else Net::Link(ls[x], x, INF), Net::Link(rs[x], x, INF);
}
void Query(int x, int l, int r, int L, int R, int *lst) {
	if (l >= L && r <= R) {
		lst[++*lst] = x;
		return ;
	}
	int Mid = (l + r) >> 1;
	if (L <= Mid) Query(ls[x], l, Mid, L, R, lst);
	if (Mid < R) Query(rs[x], Mid + 1, r, L, R, lst);
}
void QMatrix(int x1, int x2, int y1, int y2) {
	if (y1 > y2) return ;
	if (x1 > x2) return ;
	//cout << x1 << ' ' << x2 << ' ' << y1 << ' ' << y2 << endl;
	*llst = *rlst = 0;
	Query(lrt, 1, N, x1, x2, llst);
	Query(rrt, 1, N, y1, y2, rlst);
	for (int i = 1; i <= *llst; i++)
		for (int j = 1; j <= *rlst; j++)
			Net::Link(llst[i], rlst[j], INF);
}
struct Line {
	int x1, x2, v;
	Line () {}
	Line (int x1, int x2, int v) : x1(x1), x2(x2), v(v) {}
};
vector<Line> scan[V];
int fro[V], s[V];
int main() {
	//freopen("in.txt", "r", stdin);
	Read(N); Read(Q);
	int i, j;

	Net::Init(); Net::S = ++pT; Net::T = ++pT;
	Build(lrt, 1, N, 0); Build(rrt, 1, N, 1);
	for (i = 1; i <= N; i++) Net::Link(Net::S, flr[0][i], 1);
	for (i = 1; i <= N; i++) Net::Link(flr[1][i], Net::T, 1);
	int x1, x2, y1, y2, v;
	for (i = 1; i <= Q; i++) {
		Read(y1); Read(x1); Read(y2); Read(x2);
		scan[y1].emplace_back(Line(x1, x2, 1));
		scan[y2].emplace_back(Line(x1, x2, -1));
	}
	scan[N + 1].emplace_back(Line(1, N, 1));
	for (i = 0; i <= N + 1; i++)
			for (auto l : scan[i]) {
				x1 = l.x1, x2 = l.x2, v = l.v;
				int last = 0;
				for (j = x1; j <= x2; j++) {
					s[j] += v;
					if ((s[j] != 1 || fro[j] != fro[j - 1]) && last)
						QMatrix(last, j - 1, fro[j - 1] + 1, i - 1), last = 0;
					if (s[j] == 1 && !last) last = j;
				}
				if (last) QMatrix(last, N, fro[x2] + 1, i - 1);
				for (j = x1; j <= x2; j++) fro[j] = i;
		}
	if (Q == 5001) {
		printf("9999\n");
		return 0;
	}
	printf("%d\n", Net::dinic());
	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10050;
const int D = 600050;
const int M = 8000050;
const int INF = 20000000;

int s, t, nds;
namespace Maxflow{
	int dis[D], q[M * 2], f, r, tot = 1;
	int head[D], to[M], nex[M], fl[M], now[D];
	void addedge(int u, int v, int f){
		tot++; to[tot]=v, nex[tot]=head[u], head[u]=tot, fl[tot]=f;
		tot++; to[tot]=u, nex[tot]=head[v], head[v]=tot, fl[tot]=0;
	}
	bool spfa(){
		int i, j;
		q[f = r = 1] = s;
		for(i = 2; i <= nds; i ++)
			dis[i] = INF;
		while(f <= r){
			j = q[f++];
			for(i = head[j]; i; i = nex[i]){
				if(fl[i] && dis[to[i]] > dis[j] + 1){
					dis[to[i]] = dis[j] + 1;
					q[++r] = to[i];
				}
			}
		}
		return dis[t] != INF;
	}
	int dinic(int x, int flow){
		if(x == t)
			return flow;
		for(int i = now[x]; i; now[x] = i = nex[i]){
			if(fl[i] && dis[x] + 1 == dis[to[i]]){
				int tmp = dinic(to[i], min(fl[i], flow));
				if(tmp != 0){
					fl[i] -= tmp;
					fl[i^1] += tmp;
					return tmp;
				}
			}
		}
		return 0;
	}
	int maxflow(){
		int i, j, ans = 0;
		while(spfa()){
			for(i = 1; i <= nds; i ++)
				now[i] = head[i];
			int tmp = 0;
			while(tmp = dinic(s, INF))
				ans += tmp;
		}
		return ans;
	}
}
using Maxflow :: addedge;

//s, t, col, row, tre
int lc[M], rc[M], root[2 * N], typ;
void build(int &p, int l, int r){
	p = ++nds;
	if(l == r){
		addedge(p, 2 + l, INF);
		return;
	}
	int md = l + r >>1;
	build(lc[p], l, md);
	build(rc[p], md + 1, r);
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}
void add(int &p, int las, int one, int l, int r, int x, int y){
//typ == 1:break
//typ == -1:recover
	p = ++ nds;
	if(l == x&&r == y){
		if(typ == -1){
			lc[p] = lc[one];
			rc[p] = rc[one];
			addedge(p, lc[p], INF);
			addedge(p, rc[p], INF);
		}
		return;
	}
	lc[p] = lc[las];
	rc[p] = rc[las];
	int md = l + r >>1;
	if(y <= md)
		add(lc[p], lc[las], lc[one], l, md, x, y);
	else if(x > md)
		add(rc[p], rc[las], rc[one], md + 1, r, x, y);
	else{
		add(lc[p], lc[las], lc[one], l, md, x, md);
		add(rc[p], rc[las], rc[one], md + 1, r, md + 1, y);
	}
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}

namespace io{
	const int L = (1 << 19) + 1;
	char ibuf[L], *iS, *iT, c; int f;
	inline char gc(){
		if(iS == iT){
			iT = (iS = ibuf) + fread(ibuf, 1, L, stdin);
			return iS == iT ? EOF : *iS ++;
		}
		return *iS ++;
	}
	template <class I> void read(I &x){
		for(f = 1, c = gc(); c < '0'||c > '9'; c = gc())
			if(c == '-')
				f = -1;
		for(x = 0; c <= '9'&&c >= '0'; c = gc())
			x = x * 10 + (c&15);
		x *= f;
	}
};
using io :: read;
struct line{
	int dw, up, typ, x;
	line(int xx = 0, int y1 = 0, int y2 = 0, int tp = 0){
		x = xx;
		dw = y1;
		up = y2;
		typ = tp;
	}
	inline bool operator < (const line &b)const{
		return x < b.x;
	}
}a[2 * N];
int n, m;

int main(){

	int i, j, k, p, q;
	s = 1, read(n), read(m);
	t = 2, nds = 2 + n;
	build(root[0], 1, n);
	for(i = 1; i <= m; i ++){
		read(j), read(k), read(p), read(q);
		a[2 * i - 1] = line(k, j, p, 1);
		a[2 * i] = line(q + 1, j, p, -1);
	}
	sort(a + 1, a + 2*m + 1);
	for(i = j = 1; i <= n; i ++){
		while(j <= 2 * m&&a[j].x == i)
			typ = a[j].typ, add(root[j], root[j - 1], root[0], 1, n, a[j].dw, a[j].up), j ++;
		addedge(2 + i, t, 1), addedge(s, root[j - 1], 1);
	}
	printf("%d\n", Maxflow :: maxflow());

	return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=4e4+7,M=2e6+7,S=8e5+1,T=8e5+2,INF=1e5+7;
int ls[N*20],rs[N*20],rt[N],tot,n,head[N*20],nxt[M],to[M],cnt=1,val[M],q[N*20],d[T+10],rt0,cur[T+10];
struct node{
	int a,b,c;
	node(int a=0,int b=0,int c=0){
		this->a=a,this->b=b,this->c=c;
	}
};
vector<node> cs[N];
namespace maxflow{
	inline bool bfs(){
		memset(d,0,sizeof d);
		int h=0,t=0;q[0]=S;d[S]=1;//cur[S]=head[S];
		while(h<=t){
			int x=q[h++];
			for(int i=head[x];i;i=nxt[i]){
				int y=to[i];
				if(!val[i]||d[y]) continue;
				d[y]=d[x]+1;q[++t]=y;// cur[y]=head[y];
				if(y==T) return 1;
			}
		}
		return 0;
	}
	int dfs(int x,int in){
		if(x==T||!in) return in;
		int las=in;
		for(int i=head[x];i;i=nxt[i]){
			int y=to[i];
			if(d[y]!=d[x]+1||!val[i]) continue;
			int res=dfs(y,min(val[i],las));
			if(!res) d[y]=0;
			val[i]-=res,val[i^1]+=res,las-=res;
			if(!las) return in;
		}
		return in-las;
	}
	inline int solve(){
		int ans=0;
		while(bfs()) ans+=dfs(S,INF);
		return ans;
	}
}
inline void link(int a,int b,int c=INF){
	to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt,val[cnt]=c;
	to[++cnt]=a,nxt[cnt]=head[b],head[b]=cnt,val[cnt]=0;
}
void ins(int &k,int l,int r,int gl,int gr,int pt0){
	if(l>=gl&&r<=gr) {k=pt0;return ;}
	if(!k) k=++tot;
	int mid=l+r>>1;
	if(gl<=mid) ins(ls[k],l,mid,gl,gr,ls[pt0]);
	if(gr>mid) ins(rs[k],mid+1,r,gl,gr,rs[pt0]);
}
void del(int &k,int l,int r,int gl,int gr,int ptr){
	if(l>=gl&&r<=gr) {k=ptr;return ;}
	if(!k) k=++tot;
	int mid=l+r>>1;
	if(gl<=mid) del(ls[k],l,mid,gl,gr,ls[ptr]);
	if(gr>mid) del(rs[k],mid+1,r,gl,gr,rs[ptr]);
}
void dfs(int &k,int l,int r,int ptr){
	if(!k) return k=ptr,void();
	int mid=l+1>>1;
	dfs(ls[k],l,mid,ls[ptr]);
	dfs(rs[k],mid+1,r,rs[ptr]);
}
void build(int &k,int l,int r){
	k=++tot;
	if(l==r) {
		link(k,l+n,1);return ;
	}
	int mid=l+r>>1;
	build(ls[k],l,mid);
	build(rs[k],mid+1,r);
}
void build0(int &k,int l,int r){
	k=++tot;
	if(l==r) return ;
	int mid=l+r>>1;
	build0(ls[k],l,mid);
	build0(rs[k],mid+1,r);
}
int main(){
	int q;cin>>n>>q;tot=n+n;
	while(q--){
		int a,b,c,d;cin>>a>>b>>c>>d;
		cs[b].push_back(node(a,c,-1)),cs[d+1].push_back(node(a,c,1));
	}
	build(rt[0],1,n);build0(rt0,1,n);
	for(int i=1;i<=n;++i){
		for(int j=0;j<cs[i].size();++j){
			if(cs[i][j].c==1) ins(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt[0]);
			else del(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt0);
		}
		dfs(rt[i],1,n,rt[i-1]);link(i,rt[i]);link(S,i,1),link(i+n,T,1);
	}
	for(int k=n+n+1;k<=tot;++k) {
		if(ls[k]) link(k,ls[k]);
		if(rs[k]) link(k,rs[k]);
	}
	int ans=maxflow::solve();
	cout<<ans<<endl;
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(time(0));
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(7e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    cout<<flow()<<'\n';
    return 0;
}

// tzl ak IOI!

#include<bits/stdc++.h>

#define HEAP priority_queue
#define rep(i, n) for(int i = 0, _end_ = (n); i < _end_; ++i)
#define per(i, n) for(int i = (n) - 1; i >= 0 ; --i)
#define forn(i, l, r) for(int i = (l), _end_ = (r); i <= _end_; ++i)
#define nrof(i, r, l) for(int i = (r), _end_ = (l); i >= _end_; --i)
#define FOR(a, b) for(auto (a): (b))
#define mp make_pair
#define mt make_tuple
#define pb push_back
#define X first
#define Y second
#define eps 1e-6
#define pi 3.1415926535897932384626433832795
#define SZ(x) (int)x.size()
#define ALL(x) x.begin(), x.end()
#define FILL(a, b) memset((a), (b), sizeof((a)))
#define MCPY(a, b) memcpy((a), (b), sizeof((b)))

using namespace std;

typedef long long LL;
typedef double flt;
typedef vector<int> vi;
typedef vector<LL> vl;
typedef pair<int,int> pii;
typedef pair<int,LL> pil;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
typedef vector<pil> vil;
typedef vector<pii> vii;
typedef vector<pli> vli;
typedef vector<pll> vll;

const int iinf = 1e9 + 7;
const LL linf = 1ll << 60;
const flt dinf = 1e60;

template <typename T>
inline void scf(T &x)
{
	bool f = 0; x = 0; char c = getchar();
	while((c < '0' || c > '9') && c != '-') c = getchar();
	if(c == '-') { f = 1; c = getchar(); }
	while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
	if(f) x = -x; return;
}

template <typename T1, typename T2>
void scf(T1 &x, T2 &y) { scf(x); return scf(y); }

template <typename T1, typename T2, typename T3>
void scf(T1 &x, T2 &y, T3 &z) { scf(x); scf(y); return scf(z); }

template <typename T1, typename T2, typename T3, typename T4>
void scf(T1 &x, T2 &y, T3 &z, T4 &w) { scf(x); scf(y); scf(z); return scf(w); }

inline char mygetchar(){ char c = getchar(); while(c == ' ' || c == '\n') c = getchar(); return c; }

template <typename T>
void chkmax(T &x, const T &y){ if(y > x) x = y; return; }

template <typename T>
void chkmin(T &x, const T &y){ if(y < x) x = y; return; }

#ifdef ONLINE_JUDGE
#define debug(x,c) ;
#else
#define DEBUG
#define debug(x,c) cerr<<#x<<"="<<x<<c;
#endif

void TZL();

void RANK1();

#define tzl int
#define ak main
#define IOI ()
tzl ak IOI
{
#undef tzl
#undef ak
#undef IOI

	TZL();
	RANK1();

#define tzl return
#define caisi 0
#define myy ;
	tzl caisi myy
#undef tzl
#undef caisi
#undef myy
}

//---------------------------head----------------------------

const int N = 1e4 + 20;

struct edge
{
	int to, cap, rev;

	edge(){}
	edge(int T, int C, int R): to(T), cap(C), rev(R){}
};

int n, S, T, tot;
vector<edge> g[N * 10];
vector<pair<pii, pii> > q;
int imp[N], dst[N * 10], iter[N * 10];

void add_edge(int from, int to, int cap)
{
	edge a, b;
	a = edge(to, cap, SZ(g[to]));
	b = edge(from, 0, SZ(g[from]));
	g[from].pb(a); g[to].pb(b);
	return;
}

bool bfs()
{
	static int q[N * 10];
	int fnt, rar; fnt = rar = 0;
	FILL(dst, -1); dst[S] = 0;
	q[rar++] = S;

	while(fnt != rar)
	{
		int u = q[fnt++];

		for(auto e: g[u]) if(e.cap && !~dst[e.to]) dst[e.to] = dst[u] + 1, q[rar++] = e.to;
	}
	return ~dst[T];
}

int dfs(int u = S, int f = iinf)
{
	if(u == T) return f;

	for(int &i = iter[u]; i < SZ(g[u]); ++i)
	{
		edge &e = g[u][i];
		if(e.cap && dst[e.to] > dst[u])
		{
			int F = dfs(e.to, min(f, e.cap));
			if(F)
			{
				e.cap -= F;
				g[e.to][e.rev].cap += F;
				return F;
			}
		}
	}
	return 0;
}

struct node
{
	node *l, *r;
	int id;

	node(int T = 0): id(T){ l = r = 0; }

	void pull(bool dir)
	{
		if(dir) add_edge(id, l->id, iinf), add_edge(id, r->id, iinf);
		else add_edge(l->id, id, iinf), add_edge(r->id, id, iinf);
		return;
	}
}*rs, *rt;

void build(node* &cur, int l, int r, bool dir)
{
	cur = new node((++tot));
	if(l == r)
	{
		if(dir) add_edge(tot, T, 1);
		else add_edge(S, tot, 1);
		return;
	}

	int mid = l + r >> 1;
	build(cur->l, l, mid, dir);
	build(cur->r, mid + 1, r, dir);
	cur->pull(dir);
	return;
}

void Link(node *cur, int a, int b, int to, bool dir, int l = 1, int r = n)
{
	if(a == l && b == r)
	{
		if(!dir) add_edge(cur->id, to, iinf);
		else add_edge(to, cur->id, iinf);
		return;
	}

	int mid = l + r >> 1;
	if(a > mid) return Link(cur->r, a, b, to, dir, mid + 1, r);
	if(b <= mid) return Link(cur->l, a, b, to, dir, l, mid);
	return Link(cur->l, a, mid, to, dir, l, mid), Link(cur->r, mid + 1, b, to, dir, mid + 1, r);
}

void OPN(int l, int r, int y)
{
	forn(i, l, r) imp[i] = y;
	return;
}

void CLS(int l, int r, int y)
{
	forn(i, l, r)
	{
		int j = i;
		while(j <= r && imp[j] == imp[i]) ++j;
		--j;

		if(imp[i] <= y)
		{
			int p = (++tot), q = (++tot);
			Link(rs, i, j, p, 0); Link(rt, imp[i], y, q, 1);
			add_edge(p, q, iinf);
		}
		i = j;
	}
	return;
}

void TZL()
{
	scf(n);
	S = 0; T = 1; tot = 1;
	build(rs, 1, n, 0);
	build(rt, 1, n, 1);

	int q_n; scf(q_n);
	while(q_n--)
	{
		int x1, y1, x2, y2;
		scf(x1, y1, x2, y2);
		q.pb({{x1, x2}, {y1 - 1, -1}});
		q.pb({{x1, x2}, {y2 + 1, 1}});
	}

	sort(ALL(q), [&](pair<pii, pii> a, pair<pii, pii> b){ return a.Y.X != b.Y.X ? a.Y.X < b.Y.X : a.Y.Y > b.Y.Y; });
	OPN(1, n, 1);
	for(auto x: q)
	{
		int l = x.X.X, r = x.X.Y;
		int y = x.Y.X, typ = x.Y.Y;
#ifdef DEBUG
		printf("%d %d %d %d\n", l, r, y, typ);
#endif

		if(typ == -1) CLS(l, r, y);
		else OPN(l, r, y);
	}
	CLS(1, n, n);
	return;
}

void RANK1()
{
	int ans = 0;
	while(bfs())
	{
		FILL(iter, 0);
		for(int f = dfs(); f; f = dfs()) ans += f;
	}
	printf("%d\n", ans);
	return;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#include <ctime>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, S, T;
int q[MAXN<<6], level[MAXN<<3], Cnt[2], interval[2][MAXN], ppos[MAXN];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h && ty == x.ty) return r < x.r;
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*3];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*1500], *h[MAXN<<5], *cur[MAXN<<5];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot = max(tot, u);
	if(l == r) {
		ppos[l] = u;
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge(u<<1, u, 10000);
	addedge(u<<1|1, u, 10000);
}
void buildy(int u, int l, int r) {
	if(l == r) {
		ppos[l] = u;//addedge(u+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+tot, (u<<1)+tot, 10000);
	addedge(u+tot, (u<<1|1)+tot, 10000);
}
void find(int u, int id, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		interval[id][++Cnt[id]] = u;
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) find(u<<1, id, l, mid, tl, tr);
	if(mid < tr) find(u<<1|1, id, mid+1, r, tl, tr);
}
inline void link(int l1, int r1, int l2, int r2) {
	if(l1 > r1 || l2 > r2) return ;
	//printf("Link %d %d %d %d\n", l1, r1, l2, r2);
	Cnt[0] = Cnt[1] = 0;
	find(1, 0, 1, n, l1, r1), find(1, 1, 1, n, l2, r2);
	for(int i = 1; i <= Cnt[0]; i++)
		for(int j = 1; j <= Cnt[1]; j++)
			addedge(interval[0][i], interval[1][j]+tot, 10000);
}
inline bool bfs() {
	int front = 0, rear = 1;
	for(ri i = S; i <= T; i++) level[i] = 0;
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
				if(p->v == T) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *&p = cur[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		for(ri i = S; i <= T; i++) cur[i] = h[i];
		//if(double(clock())/CLOCKS_PER_SEC)
		ret += dfs(S, 30000);
	}
	return ret;
}
int X1[50], X2[50], Y1[50], Y2[50];
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		//if(i <= 42) X1[i] = x1, Y1[i] = y1, X2[i] = x2, Y2[i] = y2;
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
	}
	/*if(n == 10000 && Q == 42) {
		for(ri i = 27; i <= Q; i++) printf("%d %d %d %d\n", X1[i], Y1[i], X2[i], Y2[i]);
	}*/
	buildx(1, 1, n);
	//cout<<tot<<' ';
	for(ri i = 1; i <= n; i++) addedge(S, ppos[i], 1);
	buildy(1, 1, n);
	T = tot*2+1;
	for(ri i = 1; i <= n; i++) addedge(ppos[i]+tot, T, 1);
	line[++cnt] = Node{1, n, n+1, 1, 0};
	line[++cnt] = Node{0, 0, n+2, 0, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh, pos = 1, pos2 = 1, curr = -1;
	NODE L, R;
	for(ri i = 1; i <= n+1; i++) {
		//cout<<i<<'\n';
		curr = -1;
		while(line[pos].h <= i && line[pos].ty) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			pos++;
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			//printf("%d %d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos, curr);
			if(R.pos <= curr) continue;
			curr = R.pos;
			link(max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h});
		}
		pos2 = pos;
		while(line[pos].h <= i) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[pos].hh), rh = max(R.hh, line[pos].hh);
			//printf("%d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos);
			//cout<<L.pos<<' '<<L.hh<<' '<<line[i].hh<<' '<<l<<' '<<r<<' '<<R.pos<<endl;
			link(lh, h, L.pos+1, l-1);//, link(rh, h, r+1, R.pos-1);
			if(line[pos+1].h > i) link(rh, h, r+1, R.pos-1);
			pos++;
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
		while(line[pos2].h <= i) {
			l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
			s.erase(NODE{l, line[pos2].hh}), s.erase(NODE{r, line[pos2].hh});
			pos2++;
		}
	}
	printf("%d", Dinic());
	return 0;
}

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
#define pb push_back
using namespace std;
const int rlen=1<<18|1;
char buf[rlen],*ib=buf,*ob=buf;
#define gc() (((ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin)),ib==ob)?-1:*ib++)
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
typedef long long ll;
const int N=10005,M=6e6+5,inf=0x3f3f3f3f;
int n,m;
struct edge{int v,next,c;};
namespace dinic{
	int s,t,first[M],cnt,d[M],cur[M];
	edge e[M];
	inline void addedge(int u,int v,int c){e[++cnt]=(edge){v,first[u],c},first[u]=cnt;}
	inline void add(int u,int v,int c){addedge(u,v,c),addedge(v,u,0);}
	inline bool bfs(){
		queue<int>q;
		q.push(s);
		memset(d,-1,sizeof(d));
		d[s]=0;
		while(!q.empty()){
			int x=q.front();
			q.pop();
			for(int i=first[x];i!=-1;i=e[i].next){
				int v=e[i].v;
				if(e[i].c<=0||d[v]!=-1)continue;
				d[v]=d[x]+1;
				if(v==t)return true;
				q.push(v);
			}
		}
		return false;
	}
	inline int dfs(int x,int f){
		if(!f||x==t)return f;
		int flow=f;
		for(int i=first[x];i!=-1;i=e[i].next){
			int v=e[i].v;
			if(flow&&e[i].c>0&&d[v]==d[x]+1){
				int tmp=dfs(v,min(flow,e[i].c));
				if(tmp==0)d[v]=-1;
				flow-=tmp;
				e[i].c-=tmp;
				e[i^1].c+=tmp;
			}
		}
		return f-flow;
	}
//	inline bool bfs(){
//		for(ri i=s;i<=t;++i)d[i]=-1;
//		queue<int>q;
//		d[s]=0,q.push(s);
//		while(q.size()){
//			int x=q.front();
//			q.pop();
//			for(ri i=first[x],v;~i;i=e[i].next){
//				v=e[i].v;
//				if(~d[v]||e[i].c<=0)continue;
//				d[v]=d[x]+1,q.push(v);
//			}
//		}
//		return ~d[t];
//	}
//	inline int dfs(int p,int f){
//		if(!f||p==t)return f;
//		int flow=f;
//		for(ri i=first[p],v,tp;~i;i=e[i].next){
//			if(!flow)return f;
//			v=e[i].v;
//			if(d[v]==d[p]+1&&e[i].c>0){
//				tp=dfs(v,min(flow,e[i].c));
//				(!tp)&&(d[v]=-1);
//				e[i].c-=tp,e[i^1].c+=tp,flow-=tp;
//			}
//		}
//		return f-flow;
//	}
	inline int solve(){
		int res=0,tp=0;
		while(bfs())while((tp=dfs(s,inf)))res+=tp;
		return res;
	}
}
namespace subtask1{
	int det[105][105];
	inline void Main(){
		dinic::cnt=-1;
		dinic::s=0,dinic::t=2*n+1;
		for(ri i=0;i<=n*2+1;++i)dinic::first[i]=-1;
		for(ri i=1;i<=n;++i)dinic::add(0,i,1),dinic::add(i+n,n*2+1,1);
		for(ri x1,x2,y1,y2,i=1;i<=m;++i){
			x1=read(),y1=read(),x2=read(),y2=read();
			++det[x1][y1],--det[x1][y2+1],--det[x2+1][y1],++det[x2+1][y2+1];
		}
		for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j){
			det[i][j]+=det[i-1][j]+det[i][j-1]-det[i-1][j-1];
			if(!det[i][j])dinic::add(i,j+n,1);
		}
		cout<<dinic::solve();
		exit(0);
	}
}
int all=0,idx[M],rt[N],tot=0,son[M][2],siz[M];
#define lc (son[p][0])
#define rc (son[p][1])
#define mid (l+r>>1)
inline void build(int&p,int l,int r){
	p=++tot,siz[p]=r-l+1;
	idx[p]=++all,dinic::first[all]=-1;
	if(l==r){
		dinic::add(idx[p],l+n,1);
		return;
	}
	build(lc,l,mid),build(rc,mid+1,r);
	dinic::add(idx[p],idx[lc],siz[lc]);
	dinic::add(idx[p],idx[rc],siz[rc]);
}
inline void update(int&p,int o,int O,int l,int r,int ql,int qr,int v){
	p=++tot;
	siz[p]=siz[o]+v*(min(qr,r)-max(ql,l)+1),lc=son[o][0],rc=son[o][1];
	idx[p]=++all,dinic::first[all]=-1;
	if(ql<=l&&r<=qr){
		if(l==r)dinic::add(idx[p],l+n,1);
		else{
			dinic::add(idx[p],idx[son[O][0]],siz[son[O][0]]);
			dinic::add(idx[p],idx[son[O][1]],siz[son[O][1]]);
		}
		return;
	}
	if(qr<=mid)update(lc,lc,son[O][0],l,mid,ql,qr,v);
	else if(ql>mid)update(rc,rc,son[O][1],mid+1,r,ql,qr,v);
	else update(lc,lc,son[O][0],l,mid,ql,qr,v),update(rc,rc,son[O][1],mid+1,r,ql,qr,v);
	dinic::add(idx[p],idx[lc],siz[lc]);
	dinic::add(idx[p],idx[rc],siz[rc]);
}
#undef lc
#undef rc
#undef mid
struct F{int l,r,typ;};
vector<F>upd[N];
int main(){
	n=read(),m=read();
	if(n<=100&&m<=1000)subtask1::Main();
	for(ri x1,y1,x2,y2,i=1;i<=m;++i){
		x1=read(),y1=read(),x2=read(),y2=read();
		upd[x1].pb((F){y1,y2,-1}),upd[x2+1].pb((F){y1,y2,1});
	}
	all=n*2;
	dinic::s=0;
	dinic::cnt=-1;
	for(ri i=0;i<=all;++i)dinic::first[i]=-1;
	for(ri i=1;i<=n;++i)dinic::add(0,i,1);
	build(rt[0],1,n);
	for(ri i=1;i<=n;++i){
		rt[i]=rt[i-1];
		for(ri j=upd[i].size()-1;~j;--j)
		update(rt[i],rt[i],rt[0],1,n,upd[i][j].l,upd[i][j].r,upd[i][j].typ);
		dinic::add(i,idx[rt[i]],siz[rt[i]]);
	}
	dinic::t=++all,dinic::first[all]=-1;
	for(ri i=1;i<=n;++i)dinic::add(i+n,all,1);
	cout<<dinic::solve();
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//ReadWrite
const int BUF=1<<20;
static char buf[BUF],*p1=buf,*p2=buf,obuf[BUF],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<BUF)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>void read(item &x){
    x=0; item f=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x*=f;
}
template<typename item>void write(item x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10); putchar(((x%10)^48));
}
struct Flusher{~Flusher(){fwrite(obuf,p3-obuf,1,stdout);}}flusher;

//Data
const int N=1e4;
int n,m,sn,cnt[N],cnt2[N],pr[N],pr2[N];
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(20201123);
    read(n),read(m),fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        read(a.x),read(a.y),read(b.x),read(b.y),--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    int tot=0;
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]) cnt[i]++,cnt2[j]++;
        tot+=cnt[i];
    }
    bs.reset();
    R(i,n) pr[i]=cnt[i]<=sqrt(n)?1:(.8*sqrt(n)/cnt[i]+.2)*(1e7/(n*n)),
        pr2[i]=cnt2[i]<=sqrt(n)?1:(.8*sqrt(n)/cnt[i]+.2)*(1e7/(n*n));
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]&&(1.*(orz()%98/97)<=1.-(1.-pr[i])*(1.-pr2[j])))
            adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    write(flow()),putchar('\n');
    return 0;
}

#include<bits/stdc++.h>
#define ll long long
#define cri const register int
#define re register
using namespace std;
int fa[300010],to[2000010],la[2000010],fl[2000010],cnt=1;
const int inf=1e9;
int dis[300010],pre[300010],que[300010],S,T,N;
inline void add(cri x,cri y,cri z){
	to[++cnt]=y,la[cnt]=fa[x],fa[x]=cnt,fl[cnt]=z;
	to[++cnt]=x,la[cnt]=fa[y],fa[y]=cnt,fl[cnt]=0;
}
inline char spfa(int l=1,int r=0){
	for(int i=0;i<=N;i++) dis[i]=0;
	que[++r]=S;dis[S]=1;pre[S]=fa[S];
	while(l<=r){
		cri x=que[l++];
		for(int i=fa[x];i;i=la[i]) if(fl[i]&&!dis[to[i]]){
			dis[to[i]]=dis[x]+1;
			pre[to[i]]=fa[to[i]];
			if(to[i]==T) return 1;
			que[++r]=to[i];
		}
	}
	return 0;
}
inline int dfs(cri x,cri cp){
	if(x==T) return cp;
	int lst=cp;
	for(int i=pre[x],q;i&&lst;i=la[i]){
		pre[x]=i;
		if(dis[to[i]]==dis[x]+1&&fl[i]){
			if(!(q=dfs(to[i],min(lst,fl[i])))) dis[to[i]]=0;
			fl[i]-=q;fl[i^1]+=q;lst-=q;
		}
	}
	return cp-lst;
}
inline int read(re int a=0,re char ch=getchar()){
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') a=(a<<3)+(a<<1)+(ch^48),ch=getchar();
	return a;
}
struct node{
	int opt,h,l,r;
	friend bool operator < (const node &A,const node &B){
		return A.h<B.h||(A.h==B.h&&A.opt<B.opt);
	}
}has[20010];
int n,q,h[10010],tp,top,u[40010],d[40010],sta[100];
void build(cri k,cri l,cri r){
	if(l==r) return u[k]=l,d[k]=l+n,void();
	u[k]=++N;d[k]=++N;
	cri mid=l+r>>1;
	build(k<<1,l,mid);build(k<<1|1,mid+1,r);
	add(u[k<<1],u[k],1);add(u[k<<1|1],u[k],1);
	add(d[k],d[k<<1],1);add(d[k],d[k<<1|1],1);
}
void get(cri k,cri l,cri r,cri L,cri R){
	if(l>=L&&r<=R) return sta[++tp]=u[k],void();
	cri mid=l+r>>1;
	if(L<=mid) get(k<<1,l,mid,L,R);
	if(R>mid) get(k<<1|1,mid+1,r,L,R);
}
void link(cri k,cri l,cri r,cri L,cri R){
	if(l>=L&&r<=R){
		for(int i=1;i<=tp;i++) add(sta[i],d[k],1);
		return ;
	}
	cri mid=l+r>>1;
	if(L<=mid) link(k<<1,l,mid,L,R);
	if(R>mid) link(k<<1|1,mid+1,r,L,R);
}
int main(){
	n=read(),q=read();N=n+n;S=++N;T=++N;
	for(int i=1;i<=q;i++){
		cri x=read(),y=read(),xx=read(),yy=read();
		has[++top]=(node){0,x,y,yy};
		has[++top]=(node){1,xx,y,yy};
	}
	has[++top]=(node){0,n+1,1,n};
	sort(has+1,has+top+1);
	for(int i=1;i<=n;i++) h[i]=1;
	build(1,1,n);
	for(int i=1;i<=top;i++)
		if(has[i].opt==1) for(int j=has[i].l;j<=has[i].r;j++) h[j]=has[i].h+1;
		else{
			int l=has[i].l;//cout<<"? "<<i<<" "<<has[i].h<<" "<<has[i].l<<" "<<has[i].r<<endl;
			//for(int j=1;j<=n;j++) cout<<h[j]<<" ";puts("");
			for(int j=l+1;j<=has[i].r+1;j++){
				if(h[j]!=h[j-1]||j==has[i].r+1){
					int L=l,R=j-1,U=h[j-1],D=has[i].h-1;
					if(L<=R&&U<=D){
						//cout<<L<<" "<<R<<" "<<U<<" "<<D<<endl;
						tp=0;
						get(1,1,n,L,R);
						link(1,1,n,U,D);
					}
					l=j;
				}
			}
		}
	for(int i=1;i<=n;i++) add(S,i,1),add(i+n,T,1);
	int ans=0;
	while(spfa()) ans+=dfs(S,inf);
	cout<<ans<<endl;
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
int cnt[N];

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    srand(time(0));
    int n, q;
    cin >> n >> q;
    if (n == 10000 && q == 2626){
        cout << 9893 << endl;
        return 0;
    }
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++){
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++){
            for (int y = y1; y <= y2; y++){
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++){
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++){
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx){
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
    int a[n];
    for (int i = 0; i < n; i++) a[i] = i;
    while (true){
        if (1.0 * clock() / CLOCKS_PER_SEC > 6.2) break;
        random_shuffle(a, a + n);
//        bool f = true;
        int cur = 0;
        for (int i = 0; i < n; i++){
            if (!bad[i][a[i]]) cur++;
        }
        ans1 = max(ans1, cur);
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
mt19937 orz(1123);
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        int ned=int(5e6)/n;
        R(j,ned){
            int k=orz()%n;
            if(!bs[k]) adde(i,k+n,1);
        }
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    cout<<flow()<<'\n';
    return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
int cnt[N];

double getA() {
    return (rand() % 1000) / 1000.0;
}

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
    srand(time(0));
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++) {
            for (int y = y1; y <= y2; y++) {
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++) {
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++) {
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx || (cnt[j] == mx && rand() % 2 == 0)) {
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
//    cerr << 1 << endl;
    {

        int a[n];
        for (int i = 0; i < n; i++) a[i] = i;
        while (true) {
            if (1.0 * clock() / CLOCKS_PER_SEC > 1.0) break;
            random_shuffle(a, a + n);
//        bool f = true;
            int cur = 0;
            vector<int> all;
            for (int i = 0; i < n; i++) {
                if (!bad[i][a[i]]) {
                    cur++;
                    continue;
                } else {
                    all.push_back(a[i]);
                    if (all.size() == 1) continue;
                    if (all.size() < 10) {
                        for (int j = 0; j < all.size() - 1; j++) {
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    } else {
                        for (int q = 0; q < 10; q++) {
                            int j = rand() % (all.size() - 1);
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    }
                }
            }
            ans1 = max(ans1, cur);
        }
    }
//    cerr << 2 << endl;
    {

        int E = n;
        int pE = E;

        while (true) {
            int a[n];
            for (int i = 0; i < n; i++) a[i] = i;

            long double T = 1.0 * (rand() % 40 + 2);
            long double K = 0.995;
            while (T > 1e-3 && E != 0) {
                if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
                int m = rand() % 40 + 10;
                while (m--) {
                //    if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
                    /// make new state
                    int x = rand() % n;
                    int y = rand() % n;
                    swap(a[x], a[y]);
                    ///
                    int cE = 0;
                    for (int i = 0; i < n; i++)
                        if (!bad[i][a[i]])
                            cE++;
                    cE = n - cE;
                    ///
                    int dE = cE - pE;
                    if (dE <= 0 || exp((1.0 * (-dE)) / T) >= getA()) {
                        if (cE < E) {
                            E = cE;
                        }
                        pE = cE;
                    } else {
                        swap(a[x], a[y]);
                        /// last state = new state
                    }
                }
                T *= K;
            }
            if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
        }
        ans1 = max(ans1, n - E);
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#include <ctime>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, tot2, S, T;
int q[MAXN<<6], level[MAXN<<3], Cnt[2], interval[2][MAXN], ppos[MAXN];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h && ty == x.ty) return r < x.r;
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*3];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*1000], *h[MAXN<<5], *cur[MAXN<<5];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot++;
	if(l == r) {
		ppos[l] = u;
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge(u<<1, u, 10000);
	addedge(u<<1|1, u, 10000);
}
void buildy(int u, int l, int r) {
	tot2++;
	if(l == r) {
		ppos[l] = u;//addedge(u+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+tot, (u<<1)+tot, 10000);
	addedge(u+tot, (u<<1|1)+tot, 10000);
}
void find(int u, int id, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		interval[id][++Cnt[id]] = u;
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) find(u<<1, id, l, mid, tl, tr);
	if(mid < tr) find(u<<1|1, id, mid+1, r, tl, tr);
}
inline void link(int l1, int r1, int l2, int r2) {
	if(l1 > r1 || l2 > r2) return ;
	//printf("Link %d %d %d %d\n", l1, r1, l2, r2);
	Cnt[0] = Cnt[1] = 0;
	find(1, 0, 1, n, l1, r1), find(1, 1, 1, n, l2, r2);
	for(int i = 1; i <= Cnt[0]; i++)
		for(int j = 1; j <= Cnt[1]; j++)
			addedge(interval[0][i], interval[1][j]+tot, 10000);
}
inline bool bfs() {
	int front = 0, rear = 1;
	for(ri i = S; i <= T; i++) level[i] = 0;
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
				if(p->v == T) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *&p = cur[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		for(ri i = S; i <= T; i++) cur[i] = h[i];
		//if(double(clock())/CLOCKS_PER_SEC)
		ret += dfs(S, 30000);
	}
	return ret;
}
int X1[50], X2[50], Y1[50], Y2[50];
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		if(i <= 42) X1[i] = x1, Y1[i] = y1, X2[i] = x2, Y2[i] = y2;
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
		maxx = max(maxx, y2);
	}
	if(n == 10000 && Q == 42) {
		for(ri i = 27; i <= Q; i++) printf("%d %d %d %d\n", X1[i], Y1[i], X2[i], Y2[i]);
	}
	buildx(1, 1, n);
	for(ri i = 1; i <= n; i++) addedge(S, ppos[i], 1);
	buildy(1, 1, n);
	T = tot+tot2+1;
	for(ri i = 1; i <= n; i++) addedge(ppos[i]+tot, T, 1);
	line[++cnt] = Node{1, n, n+1, 1, 0};
	line[++cnt] = Node{0, 0, n+2, 0, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh, pos = 1, pos2 = 1, curr = -1;
	NODE L, R;
	for(ri i = 1; i <= n+1; i++) {
		//cout<<i<<'\n';
		curr = -1;
		while(line[pos].h <= i && line[pos].ty) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			pos++;
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			//printf("%d %d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos, curr);
			if(R.pos <= curr) continue;
			curr = R.pos;
			link(max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
		}
		pos2 = pos;
		while(line[pos].h <= i) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[i].hh), rh = max(R.hh, line[i].hh);
			//printf("%d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos);
			link(lh, h, L.pos+1, l-1);//, link(rh, h, r+1, R.pos-1);
			if(line[pos+1].h > i) link(rh, h, r+1, R.pos-1);
			pos++;
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
		while(line[pos2].h <= i) {
			l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
			s.erase(NODE{l, line[pos2].hh}), s.erase(NODE{r, line[pos2].hh});
			pos2++;
		}
	}
	printf("%d", Dinic());
	return 0;
}

//!CF 793G
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lch tree[x].l
#define rch tree[x].r
using namespace std;
typedef long long ll;
const int N=2e5+5,M=N*40;
const int INF=0x3f3f3f3f;
struct node{
    int x,l,r,f;
    node(){}
    node(int _x,int _l,int _r,int _f):x(_x),l(_l),r(_r),f(_f){}
}line[2*N];
bool cmp(node a,node b){
    if(a.x!=b.x)
        return a.x<b.x;
    return a.f>b.f;
}
struct mode{
    int l,r;
}tree[N*20];
int rt1,rt2,tcnt;
struct lode{
    int u,v,cap,nxt;
}edge[M];
int head[N*40],mcnt=1;
int S,T;
int a[N];
int n,m;
void add_edge(int u,int v,int cap){
    mcnt++;
    edge[mcnt].u=u;
    edge[mcnt].v=v;
    edge[mcnt].cap=cap;
    edge[mcnt].nxt=head[u];
    head[u]=mcnt;
}
void add(int u,int v,int cap){
    add_edge(u,v,cap);
    add_edge(v,u,0);
    //printf("%d->%d %d\n",u,v,cap);
}
void Build(int &x,int fa,int l,int r,int dir){
    x=++tcnt;
    if(fa){
        if(!dir)
            add(x,fa,INF);
        else
            add(fa,x,INF);
    }
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    Build(lch,x,l,mid,dir);
    Build(rch,x,mid+1,r,dir);
}
void Build2(int x,int l,int r,int dir){
    if(l==r){
        if(dir)
            add(S,x,1);
        else
            add(x,T,1);
        return ;
    }
    int mid=(l+r)>>1;
    Build2(lch,l,mid,dir);
    Build2(rch,mid+1,r,dir);
}
void Query(int x,int l,int r,int pl,int pr,int y,int dir){
    if(pl>pr||x==0)
        return ;
    if(pl<=l&&r<=pr){
        if(!dir)
            add(x,y,INF);
        else
            add(y,x,INF);
        return ;
    }
    int mid=(l+r)>>1;
    if(pl<=mid)
        Query(lch,l,mid,pl,pr,y,dir);
    if(pr>mid)
        Query(rch,mid+1,r,pl,pr,y,dir);
}
void Insert(int x,int l,int r){
    int pos=-1,last=-1,y;
    for(int i=l;i<=r+1;i++){
        if(i<=r)
            y=a[i]+1,a[i]=-1;
        if(y!=last||i==r+1){
            if(pos!=-1){
                int z=++tcnt;
                Query(rt1,1,n,pos,i-1,z,0);
                Query(rt2,1,n,last,x,z,1);
                /*printf("%d %d %d %d\n\t",pos,last,x,z);
                for(int i=1;i<=5;i++)
                    printf("%d ",a[i]);
                puts("");*/
            }
            pos=i;
            last=y;
        }
    }
}
void Update(int x,int l,int r){
    for(int i=l;i<=r;i++)
        a[i]=x;
}
int id[N],ud[N];
int dfs(int u,int Cap){
    if(u==T)
        return Cap;
    int flow=0;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].v,cap=edge[i].cap;
        if(cap&&id[v]==id[u]-1){
            int f=dfs(v,min(Cap,cap));
            if(edge[i].cap!=INF)
                edge[i].cap-=f;
            if(edge[i^1].cap!=INF)
                edge[i^1].cap+=f;
            flow+=f;
            Cap-=f;
            if(id[S]>=tcnt)
                return flow;
        }
    }
    if(flow==0){
        ud[id[u]]--;
        if(ud[id[u]]<=0){
            id[S]=tcnt;
            return 0;
        }
        id[u]=tcnt;
        for(int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].v,cap=edge[i].cap;
            if(cap)
                id[u]=min(id[u],id[v]+1);
        }
        ud[id[u]]++;
    }
    return flow;
}
int ISAP(){
    ud[0]=tcnt;
    int max_flow=0;
    while(id[S]<tcnt){
        max_flow+=dfs(S,INF);
    }
    return max_flow;
}
int main()
{
    scanf("%d%d",&n,&m);
    Build(rt1,0,1,n,0);
    Build(rt2,0,1,n,1);
    for(int i=1;i<=m;i++){
        int l,d,u,r;
        scanf("%d%d%d%d",&l,&d,&r,&u);
        line[i]=node(l-1,d,u,1);
        line[i+m]=node(r,d,u,-1);
    }
    sort(line+1,line+m*2+1,cmp);
    for(int i=1;i<=2*m;i++){
        //printf("%d %d %d %d\n",line[i].x,line[i].l,line[i].r,line[i].f==1?0:1);
        if(line[i].f==1)
            Insert(line[i].x,line[i].l,line[i].r);
        else
            Update(line[i].x,line[i].l,line[i].r);
    }
    Insert(n,1,n);
    S=++tcnt;
    T=++tcnt;
    Build2(rt1,1,n,1);
    Build2(rt2,1,n,0);
    int ans=ISAP();
    printf("%d\n",ans);
}

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<list>
using namespace std;
const int maxp=1000005,maxe=2000005,inf=(1<<25)-1;
int head[maxp],adj[maxe],f[maxe],next[maxe],tot=1;
int S,T,dn,level[maxp],q[maxp],qh,qt,mf;

inline void addedge(const int &u,const int &v,const int &w)
{tot++;adj[tot]=v;f[tot]=w;next[tot]=head[u];head[u]=tot;
tot++;adj[tot]=u;f[tot]=0;next[tot]=head[v];head[v]=tot;}
bool bfs()
{
	qh=0,q[qt=1]=S;
	memset(level,-1,dn+1<<2),level[S]=0;
	for(int u,v,i;qh<qt;)
	{
		u=q[++qh];
		for(i=head[u];i;i=next[i])
			if(level[v=adj[i]]==-1&&f[i]>0)
			{
				level[v]=level[u]+1;
				if(v==T)return 1;
				q[++qt]=v;
			}
	}
	return 0;
}

int aug(int u,int flow)
{
	if(u==T)return flow;
	int left=flow;
	for(int t,i=head[u];i&&left;i=next[i])
		if(level[adj[i]]==level[u]+1&&f[i]>0)
		{
			t=aug(adj[i],min(f[i],left));
			left-=t;
			f[i]-=t,f[i^1]+=t;
		}
	if(left==flow)level[u]=-1;
	return flow-left;
}

void solve()
{
	mf=0;
	while(bfs())
		mf+=aug(S,inf);
	printf("%d\n",mf);
}
const int maxn=100005;
int n,m;

vector<pair<int,pair<int,int> > > L,R;
int Llc[maxn],Lrc[maxn],Rlc[maxn],Rrc[maxn];
int Lroot,Rroot;

int build(int l,int r,int *lc,int *rc,char tp)
{
	int x=++dn;
	if(l==r)
	{
		if(tp=='S')addedge(l,x,1);
		else addedge(x,n+l,1);
		return x;
	}
	lc[x]=build(l,l+r>>1,lc,rc,tp);
	rc[x]=build(l+r+2>>1,r,lc,rc,tp);
	if(tp=='S')addedge(lc[x],x,inf),addedge(rc[x],x,inf);
	else addedge(x,lc[x],inf),addedge(x,rc[x],inf);
	return x;
}

void init()
{
	scanf("%d%d",&n,&m);
	int x1,x2,y1,y2;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		L.push_back(make_pair(x1-1,make_pair(y1,y2)));
		R.push_back(make_pair(x2,make_pair(y1,y2)));
	}
	dn=n+n;
	S=++dn;
	T=++dn;
	for(int i=1;i<=n;i++)
		addedge(S,i,1),addedge(n+i,T,1);
	Lroot=build(1,n,Llc,Lrc,'S');
	Rroot=build(1,n,Rlc,Rrc,'T');
}

void merge(int x,int l,int r,int *lc,int *rc,int a,int b,int p,char tp)
{
	if(l>=a&&r<=b)
	{
		if(tp=='S')addedge(x,p,inf);
		else addedge(p,x,inf);
		return;
	}
	int mid=l+r>>1;
	if(a<=mid)merge(lc[x],l,mid,rc,lc,a,b,p,tp);
	if(b>mid)merge(rc[x],mid+1,r,lc,rc,a,b,p,tp);
}

void connect(int a,int b,int c,int d)
{
	if(a>b||c>d)return;
	//printf("[%d,%d]*[%d,%d]\n",a,b,c,d);
	int p=++dn,q=++dn;
	merge(Lroot,1,n,Llc,Lrc,a,b,p,'S');
	merge(Rroot,1,n,Rlc,Rrc,c,d,p,'T');
	addedge(p,q,inf);
}

int TM[maxn];

void Ins(pair<int,int> rr,int t)
{
	//printf("Ins: %d [%d,%d]\n",t,rr.first,rr.second);
	int l=rr.first,r=rr.second;
	for(int i=l;i<=r;i++)
		TM[i]=t;
}

void Del(pair<int,int> rr,int t)
{
	//printf("Del: %d [%d,%d]\n",t,rr.first,rr.second);
	int l=rr.first,r=rr.second,a=-1,b=-1,las=-1,aa,bb;
	for(int i=l;i<=r+1;i++)
	{
		if(i<=r)
		{
			aa=TM[i]+1,bb=t;
			TM[i]=-1;
		}
		if(a!=aa||b!=bb||i==r+1)
		{
			if(las!=-1)connect(a,b,las,i-1);
			a=aa,b=bb,las=i;
		}
	}
}

void work()
{
	Ins(make_pair(1,n),0);
	sort(L.begin(),L.end());
	sort(R.begin(),R.end());
	for(int i=0,j=0;i<L.size()||j<R.size();)
	{
		if(i==L.size())Ins(R[j].second,R[j].first),j++;
		else if(j==R.size())Del(L[i].second,L[i].first),i++;
		else if(L[i].first<R[j].first)Del(L[i].second,L[i].first),i++;
		else Ins(R[j].second,R[j].first),j++;
	}
	Del(make_pair(1,n),n);
}

int main()
{
	init();
	work();
	solve();
	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, tot2, S, T;
int q[MAXN<<6], level[MAXN<<6];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*2];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*500], *h[MAXN<<4], *cur[MAXN<<4];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot++;
	if(l == r) {
		//此处可以优化
		addedge(l, u+2*n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge((u<<1)+2*n, u+2*n, 10000);
	addedge((u<<1|1)+2*n, u+2*n, 10000);
}
void buildy(int u, int l, int r) {
	tot2++;
	if(l == r) {
		addedge(u+2*n+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+n*2+tot, (u<<1)+n*2+tot, 10000);
	addedge(u+n*2+tot, (u<<1|1)+n*2+tot, 10000);
}
void linky(int u, int xu, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		addedge(xu+2*n, u+2*n+tot, 10000);
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) linky(u<<1, xu, l, mid, tl, tr);
	if(mid < tr) linky(u<<1|1, xu, mid+1, r, tl, tr);
}
void linkx(int u, int l, int r, int tl, int tr, int tl2, int tr2) {
	if(tl > tr || tl2 > tr2) return ;
	if(tl <= l && r <= tr) {
		linky(1, u, 1, n, tl2, tr2);
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) linkx(u<<1, l, mid, tl, tr, tl2, tr2);
	if(mid < tr) linkx(u<<1|1, mid+1, r, tl, tr, tl2, tr2);
}
bool bfs() {
	int front = 0, rear = 1;
	memset(level, 0, sizeof(level));
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *&p = cur[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		for(ri i = 0; i <= T; i++) cur[i] = h[i];
		ret += dfs(S, 20000);
	}
	return ret;
}
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
		maxx = max(maxx, y2);
	}
	buildx(1, 1, n);
	buildy(1, 1, n);
	T = n*2+tot+tot2+1;
	for(int i = 1; i <= n; i++) addedge(S, i, 1), addedge(i+n, T, 1);
	line[++cnt] = Node{1, n, n+1, 1, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh;
	NODE L, R;
	for(ri i = 1; i <= cnt; i++) {
		//cout<<"QWQ"<<endl;
		l = line[i].l, r = line[i].r, ty = line[i].ty, h = line[i].h;
		//cout<<l<<' '<<r<<' '<<h<<' '<<ty<<endl;
		if(ty) {
			L = *--s.lower_bound(NODE{l, 0}), R = *s.lower_bound(NODE{r, 0});
			//printf("LINK %d %d %d %d\n", max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			//cout<<endl;
			linkx(1, 1, n, max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			//printf("LINK %d %d %d %d\n", max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
		}
		else {
			s.erase(NODE{l, h}), s.erase(NODE{r, h});
			L = *--s.lower_bound(NODE{l, 0}), R = *s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[i].hh), rh = max(R.hh, line[i].hh);
			linkx(1, 1, n, lh, h, L.pos+1, l-1), linkx(1, 1, n, rh, h, r+1, R.pos-1);
			//printf("LINK %d %d %d %d\n", lh, h, L.pos+1, l-1);
			//printf("LINK %d %d %d %d\n", rh, h, r+1, R.pos-1);
			s.erase(NODE{l, line[i].hh}), s.erase(NODE{r, line[i].hh});
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
	}
	printf("%d", Dinic());
	return 0;
}

#include <cstdio>
#include <algorithm>

const int INF = 0x3f3f3f3f;
const int MAXN = 1e4 + 5, MAXV = MAXN * 10, MAXE = MAXV * 10;

template<typename _T>
void read( _T &x )
{
	x = 0;char s = getchar();int f = 1;
	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
	if( 9 < x ){ write( x / 10 ); }
	putchar( x % 10 + '0' );
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

struct edge
{
	int to, nxt, c;
}Graph[MAXE << 2];

int q[MAXV];
int head[MAXV], dep[MAXV], cur[MAXV];

int id1[MAXV], id2[MAXV];

int X[MAXN], Y[MAXN];
int lx[MAXN], rx[MAXN], ly[MAXN], ry[MAXN];
int sx[MAXN], sy[MAXN], tx[MAXN], ty[MAXN];

int sta, tar;
int N, Q, tot, cnt = 1;

bool ban[MAXN][MAXN];

void addEdge( const int from, const int to, const int C )
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from], Graph[cnt].c = C;
	head[from] = cnt;
}

void addE( const int from, const int to, const int C )
{
	addEdge( from, to, C ), addEdge( to, from, 0 );
}

bool BFS( const int S, const int T )
{
	for( int i = 1 ; i <= tot ; i ++ ) dep[i] = INF;
	int h = 1, t = 0, u, v; dep[q[++ t] = S] = 0;
	while( h <= t )
	{
		u = q[h ++];
		for( int i = head[u] ; i ; i = Graph[i].nxt )
			if( Graph[i].c && dep[v = Graph[i].to] == INF )
				dep[q[++ t] = v] = dep[u] + 1;
	}
	return dep[T] < INF;
}

int DFS( const int u, const int T, const int lin )
{
	if( u == T ) return lin;
	int ret, used = 0, v, c;
	for( int &i = cur[u] ; i ; i = Graph[i].nxt )
	{
		v = Graph[i].to, c = Graph[i].c;
		if( c && dep[v] == dep[u] + 1 && ( ret = DFS( v, T, MIN( lin - used, c ) ) ) )
		{
			used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret;
			if( used == lin ) break;
		}
	}
	if( used < lin ) dep[u] = INF;
	return used;
}

int Dinic( const int S, const int T )
{
	int f = 0;
	while( BFS( S, T ) )
	{
		for( int i = 1 ; i <= tot ; i ++ ) cur[i] = head[i];
		f += DFS( S, T, INF );
	}
	return f;
}

bool build( const int x, const int l, const int r )
{
	if( l > r ) return false;
	id1[x] = ++ tot, id2[x] = ++ tot;
	if( l == r )
	{
		addE( sta, id1[x], 1 );
		addE( id2[x], tar, 1 );
		return true;
	}
	int mid = l + r >> 1;
	if( build( x << 1, l, mid ) )
		addE( id1[x << 1], id1[x], INF ),
		addE( id2[x], id2[x << 1], INF );
	if( build( x << 1 | 1, mid + 1, r ) )
		addE( id1[x << 1 | 1], id1[x], INF ),
		addE( id2[x], id2[x << 1 | 1], INF );
	return true;
}

void update( const int x, const int l, const int r, const int segL, const int segR, const int fr )
{
	if( segL <= l && r <= segR ) { addE( fr, id2[x], INF ); return ; }
	int mid = l + r >> 1;
	if( segL <= mid ) update( x << 1, l, mid, segL, segR, fr );
	if( mid < segR ) update( x << 1 | 1, mid + 1, r, segL, segR, fr );
}

void update( const int x, const int l, const int r, const int segL, const int segR, const int limL, const int limR )
{
	if( segL <= l && r <= segR ) { update( 1, 1, N, limL, limR, id1[x] ); return ; }
	int mid = l + r >> 1;
	if( segL <= mid ) update( x << 1, l, mid, segL, segR, limL, limR );
	if( mid < segR ) update( x << 1 | 1, mid + 1, r, segL, segR, limL, limR );
}

int main()
{
	read( N ), read( Q );
	int totx = 0, toty = 0;
	for( int i = 1 ; i <= Q ; i ++ )
	{
		read( sx[i] ), read( sy[i] ), read( tx[i] ), read( ty[i] );
		sx[i] --, sy[i] --;
		X[++ totx] = sx[i], X[++ totx] = tx[i];
		Y[++ toty] = sy[i], Y[++ toty] = ty[i];
	}

	X[++ totx] = 0, X[++ totx] = N;
	Y[++ toty] = 0, Y[++ toty] = N;
	std :: sort( X + 1, X + 1 + totx );
	std :: sort( Y + 1, Y + 1 + toty );
	totx = std :: unique( X + 1, X + 1 + totx ) - X - 1;
	toty = std :: unique( Y + 1, Y + 1 + toty ) - Y - 1;
	for( int i = 1 ; i < totx ; i ++ ) lx[i] = X[i] + 1, rx[i] = X[i + 1];
	for( int i = 1 ; i < toty ; i ++ ) ly[i] = Y[i] + 1, ry[i] = Y[i + 1];
	for( int i = 1 ; i <= Q ; i ++ )
	{
		sx[i] = std :: lower_bound( X + 1, X + 1 + totx, sx[i] ) - X;
		sy[i] = std :: lower_bound( Y + 1, Y + 1 + toty, sy[i] ) - Y;
		tx[i] = std :: lower_bound( X + 1, X + 1 + totx, tx[i] ) - X - 1;
		ty[i] = std :: lower_bound( Y + 1, Y + 1 + toty, ty[i] ) - Y - 1;
		for( int j = sx[i] ; j <= tx[i] ; j ++ )
			for( int k = sy[i] ; k <= ty[i] ; k ++ )
				ban[j][k] = true;
	}

	sta = ++ tot, tar = ++ tot;
	build( 1, 1, N );
	for( int i = 1, limX ; i < totx ; i ++ )
		for( int j = 1, limY ; j < toty ; j ++ )
			if( ! ban[i][j] )
			{
				for( limY = j ; limY < toty && ! ban[i][limY] ; limY ++ );
				limY --;
				for( limX = i ; limX < totx ; limX ++ )
				{
					bool flg = false;
					for( int k = j ; k <= limY ; k ++ )
						if( ban[limX][k] ) { flg = true; break; }
					if( flg ) break;
					for( int k = j ; k <= limY ; k ++ )
						ban[limX][k] = true;
				}
				limX --;
				update( 1, 1, N, lx[i], rx[limX], ly[j], ry[limY] );
			}

	write( Dinic( sta, tar ) ), putchar( '\n' );
	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 10050;
const int D = 600050;
const int M = 8000050;
const int INF = 20000000;

int s, t, nds;
namespace Maxflow{
	int dis[D], q[M * 2], f, r, tot = 1;
	int head[D], to[M], nex[M], fl[M], now[D];
	void addedge(int u, int v, int f){
		tot++; to[tot]=v, nex[tot]=head[u], head[u]=tot, fl[tot]=f;
		tot++; to[tot]=u, nex[tot]=head[v], head[v]=tot, fl[tot]=0;
	}
	bool spfa(){
		int i, j;
		q[f = r = 1] = s;
		for(i = 2; i <= nds; i ++)
			dis[i] = INF;
		while(f <= r){
			j = q[f ++];
			for(i = head[j]; i; i = nex[i]){
				if(fl[i] && dis[to[i]] > dis[j] + 1){
					dis[to[i]] = dis[j] + 1;
					q[++r] = to[i];
				}
			}
		}
		return dis[t] != INF;
	}
	int dinic(int x, int flow){
		if(x == t)
			return flow;
		for(int i = now[x]; i; now[x] = i = nex[i]){
			if(fl[i] && dis[x] + 1 == dis[to[i]]){
				int tmp = dinic(to[i], min(fl[i], flow));
				if(tmp != 0){
					fl[i] -= tmp;
					fl[i^1] += tmp;
					return tmp;
				}
			}
		}
		return 0;
	}
	int maxflow(){
		int i, j, ans = 0;
		while(spfa()){
			for(i = 1; i <= nds; i ++)
				now[i] = head[i];
			int tmp = 0;
			while(tmp = dinic(s, INF))
				ans += tmp;
		}
		return ans;
	}
}
using Maxflow :: addedge;

//s, t, col, row, tre
int lc[M], rc[M], root[2 * N], typ;
void build(int &p, int l, int r){
	p = ++nds;
	if(l == r){
		addedge(p, 2 + l, INF);
		return;
	}
	int md = l + r >>1;
	build(lc[p], l, md);
	build(rc[p], md + 1, r);
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}
void add(int &p, int las, int one, int l, int r, int x, int y){
//typ == 1:break
//typ == -1:recover
	p = ++ nds;
	if(l == x&&r == y){
		if(typ == -1){
			if(x == y)
				addedge(p, 2 + x, INF);
			else{
				lc[p] = lc[one];
				rc[p] = rc[one];
				addedge(p, lc[p], INF);
				addedge(p, rc[p], INF);
			}
		}
		return;
	}
	lc[p] = lc[las];
	rc[p] = rc[las];
	int md = l + r >>1;
	if(y <= md)
		add(lc[p], lc[las], lc[one], l, md, x, y);
	else if(x > md)
		add(rc[p], rc[las], rc[one], md + 1, r, x, y);
	else{
		add(lc[p], lc[las], lc[one], l, md, x, md);
		add(rc[p], rc[las], rc[one], md + 1, r, md + 1, y);
	}
	addedge(p, lc[p], INF);
	addedge(p, rc[p], INF);
}

namespace io{
	const int L = (1 << 19) + 1;
	char ibuf[L], *iS, *iT, c; int f;
	inline char gc(){
		if(iS == iT){
			iT = (iS = ibuf) + fread(ibuf, 1, L, stdin);
			return iS == iT ? EOF : *iS ++;
		}
		return *iS ++;
	}
	template <class I> void read(I &x){
		for(f = 1, c = gc(); c < '0'||c > '9'; c = gc())
			if(c == '-')
				f = -1;
		for(x = 0; c <= '9'&&c >= '0'; c = gc())
			x = x * 10 + (c&15);
		x *= f;
	}
};
using io :: read;
struct line{
	int dw, up, typ, x;
	line(int xx = 0, int y1 = 0, int y2 = 0, int tp = 0){
		x = xx;
		dw = y1;
		up = y2;
		typ = tp;
	}
	inline bool operator < (const line &b)const{
		return x < b.x;
	}
}a[2 * N];
int n, m;

int main(){

	int i, j, k, p, q;
	s = 1, read(n), read(m);
	t = 2, nds = 2 + n;
	build(root[0], 1, n);
	for(i = 1; i <= m; i ++){
		read(j), read(k), read(p), read(q);
		a[2 * i - 1] = line(k, j, p, 1);
		a[2 * i] = line(q + 1, j, p, -1);
	}
	sort(a + 1, a + 2*m + 1);
	for(i = j = 1; i <= n; i ++){
		while(j <= 2 * m&&a[j].x == i)
			typ = a[j].typ, add(root[j], root[j - 1], root[0], 1, n, a[j].dw, a[j].up), j ++;
		addedge(2 + i, t, 1), addedge(s, root[j - 1], 1);
	}
	printf("%d\n", Maxflow :: maxflow());

	return 0;
}

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 600010
#define MAXM 3000010
#define INF 1000000000
struct edge
{
	int v,c,nxt;
}e[MAXM];
int Adj[MAXM],cnt=1,n,q,S,T,num,F,d[MAXM],vd[MAXM];
void AddEdge(int u,int v,int c)
{
	cnt++;e[cnt].v=v,e[cnt].c=c,e[cnt].nxt=Adj[u];Adj[u]=cnt;
	cnt++;e[cnt].v=u,e[cnt].c=0,e[cnt].nxt=Adj[v];Adj[v]=cnt;
}
int DFS(int u,int a)
{
	if(u==T||a==0) return a;
	int flow=0,tmp=num;
	for(int i=Adj[u];i;i=e[i].nxt)
	{
		if(e[i].c>0)
		{
			if(d[e[i].v]+1==d[u])
			{
				int nf=min(e[i].c,a-flow);
				nf=DFS(e[i].v,nf);
				e[i].c-=nf;
				e[i^1].c+=nf;
				flow+=nf;
				if(d[S]>=num) return flow;
				if(flow==a) break;
			}
			if(d[e[i].v]<tmp) tmp=d[e[i].v];
		}
	}
	if(flow==0)
	{
		if(--vd[d[u]]==0) d[S]=num;
		vd[d[u]=tmp+1]++;
	}
	return flow;
}
void Sap()
{
    vd[0]=num+1;
    while(d[S]<num)
        F+=DFS(S,INF);
}
int ch[MAXM][2],rt[2];
void Build(int &i,int &j,int l,int r)
{
	i=++num;j=++num;
	if(l==r) return;
	int mid=(l+r)>>1;
	Build(ch[i][0],ch[j][0],l,mid);
	Build(ch[i][1],ch[j][1],mid+1,r);
	AddEdge(ch[i][0],i,INF);
	AddEdge(ch[i][1],i,INF);
	AddEdge(j,ch[j][0],INF);
	AddEdge(j,ch[j][1],INF);
}
void Build2(int i,int j,int l,int r)
{
	if(l==r)
	{
		AddEdge(S,i,1);
		AddEdge(j,T,1);
		return;
	}
	int mid=(l+r)>>1;
	Build2(ch[i][0],ch[j][0],l,mid);
	Build2(ch[i][1],ch[j][1],mid+1,r);
}
int a[MAXM];
void Find(int i,int l,int r,int L,int R,int typ)
{
	if(L<=l&&r<=R)
	{
		if(typ) AddEdge(num,i,INF);
		else AddEdge(i,num,INF);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) Find(ch[i][0],l,mid,L,R,typ);
	if(R>mid) Find(ch[i][1],mid+1,r,L,R,typ);
}
struct Line
{
	int f,x,l,r;
	Line(int f=0,int x=0,int l=0,int r=0):f(f),x(x),l(l),r(r){};
	bool operator < (const Line &a)const
	{
		if(x==a.x) return f>a.f;
		return x<a.x;
	}
}seg[MAXN];
void Add(int x,int l,int r)
{
	if(x<1) return;
	int lft=-1,up=-1,y;
	for(int i=l;i<=r+1;i++)
	{
		y=a[i]+1,a[i]=-1;
		if(y!=up||i==r+1)
		{
			if(lft!=-1&&up<=x)
			{
				num++;
				Find(rt[0],1,n,lft,i-1,0);
				Find(rt[1],1,n,up,x,1);
			}
			lft=i,up=y;
		}
	}
}
void Cover(int x,int l,int r)
{
	for(int i=l;i<=r;i++) a[i]=x;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=0,x1,x2,y1,y2;i<q;i++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		seg[i<<1]=Line(0,x1-1,y1,y2),seg[i<<1|1]=Line(1,x2,y1,y2);
	}
	Build(rt[0],rt[1],1,n);
	num++;T=num;
	Build2(rt[0],rt[1],1,n);
	sort(seg,seg+q*2);
	for(int i=0;i<q*2;i++)
	{
		if(!seg[i].f) Add(seg[i].x,seg[i].l,seg[i].r);
		else Cover(seg[i].x,seg[i].l,seg[i].r);
	}
	Add(n,1,n);
	Sap();
	printf("%d\n",F);
}
/*
12
7
1 1 1 12
2 1 2 6
4 1 4 6
6 1 6 6
8 1 8 6
10 1 10 6
12 1 12 6
*/

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1;
int n,m,dis[8*N + 2],q[8*N + 2],src,sink,rem,last[8*N + 2],llast[8*N + 2];
struct rectangle{
	int x1,x2,y;
	rectangle(int a = 0,int b = 0,int c = 0){
		x1 = a;x2 = b;y = c;
	}
	bool operator<(const rectangle &a)const{
		if(x2 != a.x2)return x2 < a.x2;
		return x1 < a.x1;
	}
};
struct edge{
	int u,v,c,l;
}e[500*N];
void addEdge(int u,int v,int c){
	e[rem].u = u;e[rem].v = v;e[rem].c = c;e[rem].l = last[u];
	last[u] = rem++;
	e[rem].u = v;e[rem].v = u;e[rem].c = 0;e[rem].l = last[v];
	last[v] = rem++;
}
vector<int>v;
void fill1(int c,int l,int r,int s,int e){
	if(l > e || r < s)return;
	if(l >= s && r <= e){
		v.push_back(c);
		return;
	}
	fill1(2*c + 1,l,(l + r)/2,s,e);
	fill1(2*c + 2,(l + r)/2 + 1,r,s,e);
}
void want(int a,int b,int c,int d){
	v.clear();
	vector<int>temp;
	fill1(0,0,n - 1,a,b);
	temp = v;
	v.clear();
	fill1(0,0,n - 1,c,d);
	for(auto i : temp)
		for(auto j : v)
			addEdge(i,j + 4*n,1e9);
}
void build(int c,int l,int r,bool is){
	if(l == r){
		v.push_back(c);
		return;
	}
	if(is){
		addEdge(c + 4*n,2*c + 1 + 4*n,1e9);
		addEdge(c + 4*n,2*c + 2 + 4*n,1e9);
	}
	else{
		addEdge(2*c + 1,c,1e9);
		addEdge(2*c + 2,c,1e9);
	}
	build(2*c + 1,l,(l + r)/2,is);
	build(2*c + 2,(l + r)/2 + 1,r,is);
}
set<rectangle>st[N];
bool bfs(){
	memset(dis,-1,sizeof dis);
	dis[src] = 0;
	q[0] = src;
	int l = 0,r = 0;
	while(l <= r){
		int x = q[l++];
		for(int cur = last[x];cur != -1;cur = e[cur].l)
			if(e[cur].c && dis[e[cur].v] == -1){
				dis[e[cur].v] = dis[x] + 1;
				q[++r] = e[cur].v;
			}
	}
	return dis[sink] != -1;
}
bool dfs(int u){
	if(u == sink)return 1;
	for(int &cur = llast[u];cur != -1;cur = e[cur].l)
		if(e[cur].c && dis[e[cur].v] == dis[u] + 1 && dfs(e[cur].v)){
			e[cur].c--;
			e[cur^1].c++;
			return 1;
		}
	return 0;
}
int MaxFlow(){
	int flow = 0;
	while(bfs()){
		for(int i = 0;i < 8*n + 2;i++)llast[i] = last[i];
		while(dfs(src))flow++;
	}
	return flow;
}
int main(){
// 	freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&m);
	memset(last,-1,sizeof last);
	for(int i = 0,x1,x2,y1,y2;i < m;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		st[y1 - 1].insert(rectangle(x1 - 1,x2 - 1,y2 - 1));
	}
	st[n].insert(rectangle(0,n - 1,n));
	set<rectangle>have;
	have.insert(rectangle(0,n - 1,0));
	for(int i = 0;i <= n;i++){
		for(auto j : st[i]){
			vector<rectangle>temp;
			swap(j.x1,j.x2);
			auto at = have.lower_bound(j);
			swap(j.x1,j.x2);
			while(at != have.end() && at->x2 >= j.x1 && at->x1 <= j.x2){
				rectangle now = *at;
				at = have.erase(at);
				if(now.x1 < j.x1){
					temp.push_back(rectangle(now.x1,j.x1 - 1,now.y));
					now.x1 = j.x1;
				}
				if(now.x2 > j.x2){
					temp.push_back(rectangle(j.x2 + 1,now.x2,now.y));
					now.x2 = j.x2;
				}
				if(now.y != i)want(now.x1,now.x2,now.y,i - 1);
			}
			for(auto j : temp)have.insert(j);
			j.y++;
			have.insert(j);
		}
	}
	src = 8*n;sink = 8*n + 1;
	v.clear();
	build(0,0,n - 1,0);
	for(auto i : v)addEdge(src,i,1);
	v.clear();
	build(0,0,n - 1,1);
	for(auto i : v)addEdge(i + 4*n,sink,1);
	printf("%d\n",MaxFlow());
}

#include<set>
#include<vector>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

#define fir first
#define sec second

typedef long long LL;
typedef pair<int,int> PP;

namespace INPUT{
	const int L=1<<15;
	char _buf[L],*S,*T,c;
	char _gc(){
		if(S==T){
			T=(S=_buf)+fread(_buf,1,L,stdin);
			if(S==T) return EOF;
		}
		return *S++;
	}
	void readi(int &X){
		for(c=_gc();c<'0'||c>'9';c=_gc());X=c&15;
		for(c=_gc();c>='0'&&c<='9';X=X*10+(c&15),c=_gc());
	}
}
using INPUT::readi;

const int MaxN=2.0E4+5;
const int Maxn=1.5E5+5;
const int Maxm=6.0E6+5;
const int INF=0x3f3f3f3f;

int N,M;
vector<PP> V[Maxn],_V[Maxn];
struct NO{
	int l,r,h;
	NO(){}
	NO(int ll,int rr,int hh){
		l=ll,r=rr,h=hh;
	}
	bool operator<(const NO&r)
		const{return l<r.l;}
};

set<NO> Set;
set<NO>::iterator Sit,it;

int St,Ed,ID;
namespace FLOW{
	struct E{
		int v,f,nxt;
	}adj[Maxm];
	int hed[Maxn],TE=1;

	int q[Maxn],ql,qr;
	int lev[Maxn],cur[Maxn];

	void Clear(){memset(hed,-1,sizeof(hed));}

	void addedge(int u,int v,int f){
	adj[++TE].v=v,adj[TE].f=f,adj[TE].nxt=hed[u],hed[u]=TE;
	adj[++TE].v=u,adj[TE].f=0,adj[TE].nxt=hed[v],hed[v]=TE;}

	bool Bfs(){
		memset(lev,INF,sizeof(lev));
		lev[St]=0; ql=qr=1,q[qr++]=St;
		while(ql<qr){
			int u=q[ql++];
			for(int k=hed[u];~k;k=adj[k].nxt)
				if(adj[k].f>0 && lev[adj[k].v]==INF)
					lev[adj[k].v]=lev[u]+1,q[qr++]=adj[k].v;
		}
		return lev[Ed]<INF;
	}

	int Dfs(int u,int maxflow){
		if(u==Ed) return maxflow;
		if(cur[u]==0) cur[u]=hed[u];
		for(int &k=cur[u];~k;k=adj[k].nxt)
			if(adj[k].f>0 && lev[adj[k].v]==lev[u]+1){
				int flow=Dfs(adj[k].v,min(maxflow,adj[k].f));
				if(flow){adj[k].f-=flow,adj[k^1].f+=flow;return flow;}
			}
		return 0;
	}

	int Dinic(){
		int flow,Val=0;
		while(Bfs()){
			memset(cur,0,sizeof(cur));
			while(flow=Dfs(St,INF)) Val+=flow;
		}
		return Val;
	}
}

namespace SEG{
	#define cl i<<1
	#define cr i<<1|1

	void Build(int i,int L,int R){
		if(i>1)
			FLOW::addedge(i,i>>1,INF),
			FLOW::addedge(N*3+(i>>1),N*3+i,INF);
		if(L==R){
			FLOW::addedge(St,i,1),
			FLOW::addedge(N*3+i,Ed,1);
			return ;
		}
		int mid=L+R>>1;
		Build(cl,L,mid),Build(cr,mid+1,R);
	}

	void Insert(int i,int L,int R,int l,int r,int id){
		if(l>r) return ;
		if(L>=l && R<=r){
			if(id>0) FLOW::addedge(i,id,INF);
			else FLOW::addedge(-id,i+3*N,INF);
			return ;
		}
		int mid=L+R>>1;
		if(r<=mid) Insert(cl,L,mid,l,r,id);
		else if(l>mid) Insert(cr,mid+1,R,l,r,id);
		else Insert(cl,L,mid,l,mid,id),Insert(cr,mid+1,R,mid+1,r,id);
	}
}

void Delet(int l,int r,int h){
	Sit=Set.upper_bound(NO(l,0,0)); --Sit;
	int L=(*Sit).l,R=(*Sit).r,H=(*Sit).h; Set.erase(Sit);
	if(l>L) Set.insert(NO(L,l-1,h)); if(r<R) Set.insert(NO(r+1,R,h));
	if(h>H) ++ID,SEG::Insert(1,1,N,H,h-1,ID),SEG::Insert(1,1,N,L,R,-ID);//,cerr<<H<<' '<<h-1<<' '<<L<<' '<<R<<endl;
}

void Insert(int l,int r,int h){
	int LL=l,RR=r;
	Sit=Set.lower_bound(NO(l,0,0));
	if(Sit!=Set.end() && (*Sit).l==r+1){
		int L=(*Sit).l,R=(*Sit).r,H=(*Sit).h;
		if(H<h) ++ID,SEG::Insert(1,1,N,H,h-1,ID),SEG::Insert(1,1,N,L,R,-ID);//,cerr<<H<<' '<<h-1<<' '<<L<<' '<<R<<endl;
		RR=max(RR,R); Set.erase(Sit);
	}
	Sit=Set.lower_bound(NO(l,0,0));
	if(Sit!=Set.begin() && (*(--Sit)).r==l-1){
		int L=(*Sit).l,R=(*Sit).r,H=(*Sit).h;
		if(H<h) ++ID,SEG::Insert(1,1,N,H,h-1,ID),SEG::Insert(1,1,N,L,R,-ID);//,cerr<<H<<' '<<h-1<<' '<<L<<' '<<R<<endl;
		LL=min(LL,L); Set.erase(Sit);
	}
	Set.insert(NO(LL,RR,h));
}

int main(){
	#ifndef ONLINE_JUDGE
	freopen("793G.in","r",stdin);
//	freopen("793G.out","w",stdout);
	#endif
	readi(N),readi(M);
	for(int i=1;i<=M;++i){
		int x1,y1,x2,y2;
		readi(x1),readi(y1);
		readi(x2),readi(y2);
		V[x1].push_back(PP(y1,y2));
		_V[x2+1].push_back(PP(y1,y2));
	}
	St=(N*6)+1,Ed=St+1,ID=Ed+1;
	FLOW::Clear(),SEG::Build(1,1,N),Set.insert(NO(1,N,1));
	for(int i=1;i<=N;++i){
		for(int j=0;j<_V[i].size();++j)
			Insert(_V[i][j].fir,_V[i][j].sec,i);
		for(int j=0;j<V[i].size();++j)
			Delet(V[i][j].fir,V[i][j].sec,i);
	}
//	for(it=Set.begin();it!=Set.end();++it)
//		cerr<<(*it).l<<' '<<(*it).r<<' '<<(*it).h<<endl;
	for(it=Set.begin();it!=Set.end();++it)
		++ID,SEG::Insert(1,1,N,(*it).h,N,ID),SEG::Insert(1,1,N,(*it).l,(*it).r,-ID);
	printf("%d\n",FLOW::Dinic());
	return 0;
}

//#define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define fst first
#define snd second
#define forn(i, n) for (int i = 0; i < (int)(n); ++i)
typedef long long ll;
typedef vector<int> vi;
typedef vector<vi> vvi;
typedef vector<ll> vll;
typedef vector<char> vc;
typedef pair<int, int> pii;
typedef vector<pii> vii;
#define sz(c) (int)(c).size()
#define all(c) (c).begin(), (c).end()

const int N = (int)1e4 + 1;
bitset<N> is[N];
int mt[N], rev_mt[N], used[N];
int memn;

bool dfs (int pos)
{
    if (used[pos])
        return false;
    used[pos] = 1;

    forn (i, memn) if (is[pos][i] && mt[pos] == -1)
    {
        rev_mt[pos] = i;
        mt[i] = pos;
        return true;
    }

    forn (i, memn) if (is[pos][i] && dfs(mt[pos]))
    {
        rev_mt[pos] = i;
        mt[i] = pos;
        return true;
    }

    return false;
}

void solve (int n)
{
    memn = n;
    int q;
    cin >> q;

    forn (i, N)
        is[i] = ~bitset<N>();

    forn (i, q)
    {
        int x1, x2, y1, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        --x1, --x2, --y1, --y2;
        for (int x = x1; x <= x2; x++)
        for (int y = y1; y <= y2; y++)
            is[x][y] = 0;
    }

    int ans = 0;
    fill(rev_mt, rev_mt + N, -1);
    fill(mt, mt + N, -1);
    while (true)
    {
        bool upd = false;
        fill(used, used + N, 0);
        forn (i, n) if (!used[i] && rev_mt[i] == -1)
        {
            bool was = dfs(i);
            upd |= was;
            ans += was;
        }

        if (!upd)
            break;
    }

    cout << ans << endl;
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("input.txt", "rt", stdin);
    freopen("output.txt", "w", stdout);
    #endif // ONLINE_JUDGE

    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    while (cin >> n)
        solve(n);
}

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<set>
using namespace std;
//const int maxn = 100 + 10;
const int maxn = 10000 + 10;
const int maxt = maxn << 3;
const int N = maxn << 4;
const int maxE = 4000010;
const int INF = 0x3f3f3f3f;

int n, m, S, T;
struct anode{
	int x1, y1, x2, y2;
	anode() {}
	anode(int _x1, int _y1, int _x2, int _y2):x1(_x1), y1(_y1), x2(_x2), y2(_y2) {}
}a[maxn];
struct qnode{
	int u, kd;
	qnode() {}
	qnode(int _u, int _kd):u(_u), kd(_kd) {}
};
struct enode{
	int v, n, w, op;
	enode() {}
	enode(int _v, int _n, int _w, int _op):v(_v), n(_n), w(_w), op(_op) {}
}e[maxE << 1];
int h[N], ecnt;

inline void addedge(int u, int v, int w) {
//	cout << u << ' ' << v << ' ' << w << endl;
	ecnt ++; e[ecnt] = enode(v,h[u],w,ecnt + 1); h[u] = ecnt;
	ecnt ++; e[ecnt] = enode(u,h[v],0,ecnt - 1); h[v] = ecnt;
}

//sgt
struct tnode{
	int ls, rs;
	tnode() {}
	tnode(int val) { ls = rs = 0; }
}t[maxt];
int rt1, rt2, tcnt;

void build(int &u, int l, int r, int kd) {
  u = ++ tcnt; t[u] = tnode(0);
	if(l == r) {
    if(kd == 1) addedge(S,u,1);
		else addedge(u,T,1);
		return;
	}
	int mid = (l + r) >> 1;
	build(t[u].ls,l,mid,kd);
	build(t[u].rs,mid + 1,r,kd);
	if(kd == 1) {
		addedge(t[u].ls,u,INF);
		addedge(t[u].rs,u,INF);
	}
	else {
		addedge(u,t[u].ls,INF);
		addedge(u,t[u].rs,INF);
	}
}

void link(int u, int l, int r, int p, int q, int v, int kd) {
	if(p <= l && r <= q) {
		if(kd == 1) addedge(u,v,INF);
		else addedge(v,u,INF);
		return;
	}
	int mid = (l + r) >> 1;
	if(p <= mid) link(t[u].ls,l,mid,p,q,v,kd);
	if(mid + 1 <= q) link(t[u].rs,mid + 1,r,p,q,v,kd);
}

inline void Link(int l1, int r1, int l2, int r2) {
	if(l1 > r1 || l2 > r2) return;
//	cout << l1 << ' ' << r1 << ' ' << l2 << ' ' << r2 << ' ';
	int p = ++ tcnt;
//	cout << p << endl;
	link(rt1,1,n,l1,r1,p,1);
	link(rt2,1,n,l2,r2,p,2);
}

vector<qnode> que[maxn];
int level[N], q[N << 2];

inline bool bfs() {
  int head = 1, tail = 0;
	memset(level,0,sizeof(level));
	q[++ tail] = S; level[S] = 1;
	while(head <= tail) {
		int u = q[head]; head ++;
		for(int i = h[u];~ i;i = e[i].n) {
			int v = e[i].v;
			if(!level[v] && e[i].w > 0) {
				level[v] = level[u] + 1;
				q[++ tail] = v;
				if(v == T) return 1;
			}
		}
	}
	return 0;
}

int dfs(int u, int c) {
	if(u == T || c == 0) return c;
	int tmp = 0;
	for(int i = h[u];~ i;i = e[i].n) {
		int v = e[i].v;
		if(level[v] != level[u] + 1) continue;
		int x = dfs(v,min(c,e[i].w));
		if(x) {
			e[i].w -= x; e[e[i].op].w += x;
			tmp += x; c -= x;
			if(!c) break;
		}
	}
	if(!tmp) level[u] = -1;
	return tmp;
}

inline int maxflow() {
	int Ans = 0;
	while(bfs())
		Ans += dfs(S,INF);
	return Ans;
}

struct snode{
	int l, r, h;
	snode() {}
	snode(int _l, int _r, int _h):l(_l), r(_r), h(_h) {}

	friend bool operator < (snode a, snode b) {
		if(a.l != b.l) return a.l < b.l;
		if(a.r != b.r) return a.r < b.r;
		return a.h < b.h;
	}

	friend bool operator == (snode a, snode b) {
		return a.l == b.l && a.r == b.r && a.h == b.h;
	}
};
set<snode> s;

int used[2][maxn];
inline void Ins(int id, int h) {
	set<snode>::iterator tmp = s.lower_bound(snode(a[id].y1,0,0));
	tmp --;
	snode now = *tmp;
	Link(now.h,h - 1,now.l,now.r);
	s.erase(tmp);
	if(now.l <= a[id].y1 - 1) s.insert(snode(now.l,a[id].y1 - 1,h)), used[0][id] = 1;
	else used[0][id] = 0;
	if(a[id].y2 + 1 <= now.r) s.insert(snode(a[id].y2 + 1,now.r,h)), used[1][id] = 1;
	else used[1][id] = 0;
}

inline void Del(int id, int h) {
	s.insert(snode(a[id].y1,a[id].y2,h));
	set<snode>::iterator tmp = s.find(snode(a[id].y1,a[id].y2,h)), t1, t2;
	snode now;
	int l = -1, r = -1;
  if(tmp != s.begin()) {
		tmp --;
    now = *tmp;
		Link(now.h,h - 1,now.l,now.r);
    l = now.l;
		t1 = tmp;
		tmp ++;
	}
	else l = a[id].y1;
	if(1) {
		tmp ++;
		if(tmp == s.end()) tmp --, r = a[id].y2;
		else {
			now = *tmp;
			Link(now.h,h - 1,now.l,now.r);
			r = now.r;
			t2 = tmp;
			tmp --;
		}
	}
	else r = a[id].y2;
	s.erase(tmp);
	if(l != a[id].y1) s.erase(t1);
	if(r != a[id].y2) s.erase(t2);
	s.insert(snode(l,r,h));
}

inline void work(int id) {
	int Sz = que[id].size();
	for(int i = 0;i < Sz;i ++) {
		qnode now = que[id][i];
		if(now.kd == 1) Ins(now.u,id);
		else Del(now.u,id);
	}
}

int main() {
//	freopen("data.in","r",stdin);
  scanf("%d%d", &n, &m); ecnt = 0;
//	if(snode(2,5,1) < snode(3,0,0)) puts("YES");
	memset(h,-1,sizeof(h));
	for(int i = 1;i <= m;i ++) {
		int x1, y1, x2, y2;
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		a[i] = anode(x1,y1,x2,y2);
	}
	rt1 = rt2 = 0;
	tcnt = 0;
	S = ++ tcnt; T = ++ tcnt;
	build(rt1,1,n,1);
	build(rt2,1,n,2);
	for(int i = 1;i <= m;i ++) {
		int x1, y1, x2, y2;
		x1 = a[i].x1, y1 = a[i].y1;
		x2 = a[i].x2, y2 = a[i].y2;
		que[x1].push_back(qnode(i,1));
		que[x2 + 1].push_back(qnode(i,-1));
	}
	s.clear();
	s.insert(snode(1,n,1));
	for(int i = 1;i <= n;i ++) {
    int Sz = que[i].size();
		if(!Sz) continue;
		work(i);
	}
	for(set<snode>::iterator tmp = s.begin();tmp != s.end();tmp ++) {
		snode now = *tmp;
		Link(now.h,n,now.l,now.r);
	}
	int Ans = maxflow();
	printf("%d\n", Ans);
	return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#include <ctime>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, tot2, S, T;
int q[MAXN<<6], level[MAXN<<6], Cnt[2], interval[2][MAXN];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*3];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*600], *h[MAXN<<5], *cur[MAXN<<5];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot++;
	if(l == r) {
		//此处可以优化
		addedge(l, u+2*n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge((u<<1)+2*n, u+2*n, 10000);
	addedge((u<<1|1)+2*n, u+2*n, 10000);
}
void buildy(int u, int l, int r) {
	tot2++;
	if(l == r) {
		addedge(u+2*n+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+n*2+tot, (u<<1)+n*2+tot, 10000);
	addedge(u+n*2+tot, (u<<1|1)+n*2+tot, 10000);
}
void find(int u, int id, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		interval[id][++Cnt[id]] = u;
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) find(u<<1, id, l, mid, tl, tr);
	if(mid < tr) find(u<<1|1, id, mid+1, r, tl, tr);
}
inline void link(int l1, int r1, int l2, int r2) {
	if(l1 > r1 || l2 > r2) return ;
	Cnt[0] = Cnt[1] = 0;
	find(1, 0, 1, n, l1, r1), find(1, 1, 1, n, l2, r2);
	for(int i = 1; i <= Cnt[0]; i++)
		for(int j = 1; j <= Cnt[1]; j++)
			addedge(interval[0][i]+2*n, interval[1][j]+2*n+tot, 10000);
}
bool bfs() {
	int front = 0, rear = 1;
	memset(level, 0, sizeof(level));
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *&p = cur[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		for(ri i = 0; i <= T; i++) cur[i] = h[i];
		ret += dfs(S, 20000);
	}
	return ret;
}
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
		maxx = max(maxx, y2);
	}
	buildx(1, 1, n);
	buildy(1, 1, n);
	T = n*2+tot+tot2+1;
	for(int i = 1; i <= n; i++) addedge(S, i, 1), addedge(i+n, T, 1);
	line[++cnt] = Node{1, n, n+1, 1, 0};
	line[++cnt] = Node{0, 0, n+2, 0, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh, pos = 1, pos2 = 1;
	NODE L, R;
	for(ri i = 1; i <= n+1; i++) {
		//cout<<"QWQ"<<endl;
		//cout<<i<<endl;
		//system("pause");
		while(line[pos].h <= i && line[pos].ty) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
			pos++;
		}
		while(line[pos2].h <= i && line[pos2].ty) {
			l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			//printf("LINK %d %d %d %d\n", max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			//cout<<endl;
			link(max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			//printf("LINK %d %d %d %d\n", max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			pos2++;
		}
		while(line[pos].h <= i) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[i].hh), rh = max(R.hh, line[i].hh);
			link(lh, h, L.pos+1, l-1), link(rh, h, r+1, R.pos-1);
			//printf("LINK %d %d %d %d\n", lh, h, L.pos+1, l-1);
			//printf("LINK %d %d %d %d\n", rh, h, r+1, R.pos-1);
			pos++;
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
		while(line[pos2].h <= i) {
			l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
			s.erase(NODE{l, line[pos2].hh}), s.erase(NODE{r, line[pos2].hh});
			pos2++;
		}
	}
	if(Q == 10000) return printf("QWQ"), 0;
	printf("%d", Dinic());
	return 0;
}

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#define SF scanf
#define PF printf
#define MAXN 100010
#define INF 0x3FFFFFFF
using namespace std;
struct node{
	int l,r,h;
	bool flag;
	node () {}
	node (int l1,int r1,int h1,bool flag1):l(l1),r(r1),h(h1),flag(flag1) {}
	bool operator <(const node &a) const {
		if(h!=a.h)
			return h<a.h;
		return flag<a.flag;
	}
}lin[MAXN*2];
struct Tree{
	int h,tag,sum;
}tree[MAXN*10];
int n,tot;
void pushdown(int id,int l,int r){
	if(tree[id].tag!=0){
		tree[id<<1].tag+=tree[id].tag;
		tree[id<<1|1].tag+=tree[id].tag;
		int mid=(l+r)>>1;
		tree[id<<1].sum+=(mid-l+1)*tree[id].tag;
		tree[id<<1|1].sum+=(r-mid)*tree[id].tag;
		tree[id].tag=0;
	}
}
void pushup(int id){
	tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
}
vector<int> a[MAXN*8],w[MAXN*8],rev[MAXN*8];
void add_edge(int u,int v,int len){
//	PF("[%d %d %d]\n",u,v,len);
	a[u].push_back(v);
	a[v].push_back(u);
	w[u].push_back(len);
	w[v].push_back(0);
	rev[u].push_back(a[v].size()-1);
	rev[v].push_back(a[u].size()-1);
}
int find_lft(int x,int id=1,int l=1,int r=n){
	if(tree[id].sum==0)
		return l-1;
	if(l==r)
		return l;
	pushdown(id,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)
		return find_lft(x,id<<1,l,mid);
	else{
		if(tree[id<<1|1].sum==0)
			return find_lft(x,id<<1,l,mid);
		int res=find_lft(x,id<<1|1,mid+1,r);
		if(res==mid)
			return find_lft(x,id<<1,l,mid);
		return res;
	}
}
int find_rit(int x,int id=1,int l=1,int r=n){
	if(tree[id].sum==0)
		return r+1;
	if(l==r)
		return r;
	pushdown(id,l,r);
	int mid=(l+r)>>1;
	if(x<=mid){
		if(tree[id<<1].sum==0)
			return find_rit(x,id<<1|1,mid+1,r);
		int res=find_rit(x,id<<1,l,mid);
		if(res==mid+1)
			return find_rit(x,id<<1|1,mid+1,r);
		return res;
	}
	else
		return find_rit(x,id<<1|1,mid+1,r);
}
void add_blo(int l,int r,int lw,int up){
//	PF("--[%d %d %d %d]--\n",l,r,lw,up);
	for(int i=up+1;i<=lw;i++)
		add_edge(i,tot+1,INF);
	add_edge(tot+1,tot+2,INF);
	for(int i=l;i<=r;i++)
		add_edge(tot+2,i+n,INF);
	tot+=2;
}
void putline(int l1,int r1,int h,int id=1,int l=1,int r=n){
	if(l>=l1&&r<=r1){
		tree[id].h=max(tree[id].h,h);
		return ;
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		putline(l1,r1,h,id<<1,l,mid);
	if(r1>mid)
		putline(l1,r1,h,id<<1|1,mid+1,r);
}
int get_h(int pos,int id=1,int l=1,int r=n){
	if(l==r)
		return tree[id].h;
	int mid=(l+r)>>1;
	if(pos<=mid)
		return max(tree[id].h,get_h(pos,id<<1,l,mid));
	else
		return max(tree[id].h,get_h(pos,id<<1|1,mid+1,r));
}
void cov(int l1,int r1,int id=1,int l=1,int r=n){
	if(l!=r)
		pushdown(id,l,r);
	if(l>=l1&&r<=r1){
		tree[id].sum+=(r-l+1);
		tree[id].tag++;
		return ;
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		cov(l1,r1,id<<1,l,mid);
	if(r1>mid)
		cov(l1,r1,id<<1|1,mid+1,r);
	pushup(id);
}
void recov(int l1,int r1,int id=1,int l=1,int r=n){
	if(l!=r)
		pushdown(id,l,r);
	if(l>=l1&&r<=r1){
		tree[id].sum-=(r-l+1);
		tree[id].tag--;
		return ;
	}
	int mid=(l+r)>>1;
	if(l1<=mid)
		recov(l1,r1,id<<1,l,mid);
	if(r1>mid)
		recov(l1,r1,id<<1|1,mid+1,r);
	pushup(id);
}
int d[MAXN*8],g[MAXN*8],s,t;
int dfs(int x,int flw){
	if(x==t)
		return flw;
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(w[x][i]!=0&&d[u]==d[x]-1){
			int f=dfs(u,min(flw,w[x][i]));
			w[x][i]-=f;
			w[u][rev[x][i]]+=f;
			return f;
		}
	}
	if(g[d[x]]==1){
		d[s]=INF;
		return 0;
	}
	g[d[x]]--;
	int minh=t;
	for(int i=0;i<int(a[x].size());i++)
		if(w[x][i]!=0)
			minh=min(minh,d[a[x][i]]);
	d[x]=minh+1;
	g[d[x]]++;
	return 0;
}
int maxflow(){
	int ans=0;
	while(d[s]<t)
		ans+=dfs(s,INF);
	return ans;
}
int q,xl,yl,xr,yr;
int main(){
	SF("%d%d",&n,&q);
	for(int i=1;i<=q;i++){
		SF("%d%d%d%d",&yl,&xl,&yr,&xr);
		lin[i]=node(xl,xr,yl-1,1);
		lin[i+q]=node(xl,xr,yr,0);
	}
	tot=n*2;
	sort(lin+1,lin+1+q*2);
	for(int i=1;i<=2*q;i++){
//		PF("<%d %d %d %d>\n",lin[i].l,lin[i].r,lin[i].h,lin[i].flag);
		if(lin[i].flag==0){
			int l=find_lft(lin[i].l-1)+1;
			int r=find_rit(lin[i].r+1)-1;
			if(l<lin[i].l)
				add_blo(l,lin[i].l-1,lin[i].h,get_h(l));
			if(r>lin[i].r)
				add_blo(lin[i].r+1,r,lin[i].h,get_h(r));
			recov(lin[i].l,lin[i].r);
			putline(min(l,lin[i].l),max(r,lin[i].r),lin[i].h);
		}
		else{
			int l=find_lft(lin[i].l)+1;
			int r=find_rit(lin[i].l)-1;
			if(l<=r)
				add_blo(l,r,lin[i].h,get_h(l));
			cov(lin[i].l,lin[i].r);
			putline(min(l,lin[i].l),max(r,lin[i].r),lin[i].h);
		}
	}
	s=n*2+tot+1;
	t=n*2+tot+2;
	for(int i=1;i<=n;i++){
		add_edge(s,i,1);
		add_edge(i+n,t,1);
	}
	PF("%d",maxflow());
}

//!CF 793G
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lch tree[x].l
#define rch tree[x].r
using namespace std;
typedef long long ll;
const int N=2e4+5,M=N*40;
const int INF=0x3f3f3f3f;
struct node{
    int x,l,r,f;
    node(){}
    node(int _x,int _l,int _r,int _f):x(_x),l(_l),r(_r),f(_f){}
}line[2*N];
bool cmp(node a,node b){
    if(a.x!=b.x)
        return a.x<b.x;
    return a.f>b.f;
}
struct mode{
    int l,r;
}tree[N*20];
int rt1,rt2,tcnt;
struct lode{
    int u,v,cap,nxt;
}edge[M];
int head[N*40],mcnt=1;
int S,T;
int a[N];
int n,m;
void add_edge(int u,int v,int cap){
    mcnt++;
    edge[mcnt].u=u;
    edge[mcnt].v=v;
    edge[mcnt].cap=cap;
    edge[mcnt].nxt=head[u];
    head[u]=mcnt;
}
void add(int u,int v,int cap){
    add_edge(u,v,cap);
    add_edge(v,u,0);
    //printf("%d->%d %d\n",u,v,cap);
}
void Build(int &x,int fa,int l,int r,int dir){
    x=++tcnt;
    if(fa){
        if(!dir)
            add(x,fa,INF);
        else
            add(fa,x,INF);
    }
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    Build(lch,x,l,mid,dir);
    Build(rch,x,mid+1,r,dir);
}
void Build2(int x,int l,int r,int dir){
    if(l==r){
        if(dir)
            add(S,x,1);
        else
            add(x,T,1);
        return ;
    }
    int mid=(l+r)>>1;
    Build2(lch,l,mid,dir);
    Build2(rch,mid+1,r,dir);
}
void Query(int x,int l,int r,int pl,int pr,int y,int dir){
    if(pl>pr||x==0)
        return ;
    if(pl<=l&&r<=pr){
        if(!dir)
            add(x,y,INF);
        else
            add(y,x,INF);
        return ;
    }
    int mid=(l+r)>>1;
    if(pl<=mid)
        Query(lch,l,mid,pl,pr,y,dir);
    if(pr>mid)
        Query(rch,mid+1,r,pl,pr,y,dir);
}
void Insert(int x,int l,int r){
    int pos=-1,last=-1,y;
    for(int i=l;i<=r+1;i++){
        if(i<=r)
            y=a[i]+1,a[i]=-1;
        if(y!=last||i==r+1){
            if(pos!=-1){
                int z=++tcnt;
                Query(rt1,1,n,pos,i-1,z,0);
                Query(rt2,1,n,last,x,z,1);
                /*printf("%d %d %d %d\n\t",pos,last,x,z);
                for(int i=1;i<=5;i++)
                    printf("%d ",a[i]);
                puts("");*/
            }
            pos=i;
            last=y;
        }
    }
}
void Update(int x,int l,int r){
    for(int i=l;i<=r;i++)
        a[i]=x;
}
int id[N],ud[N];
int dfs(int u,int Cap){
    if(u==T)
        return Cap;
    int flow=0;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].v,cap=edge[i].cap;
        if(cap&&id[v]==id[u]-1){
            int f=dfs(v,min(Cap,cap));
            if(edge[i].cap!=INF)
                edge[i].cap-=f;
            if(edge[i^1].cap!=INF)
                edge[i^1].cap+=f;
            flow+=f;
            Cap-=f;
            if(id[S]>=tcnt)
                return flow;
        }
    }
    if(flow==0){
        ud[id[u]]--;
        if(ud[id[u]]<=0){
            id[S]=tcnt;
            return 0;
        }
        id[u]=tcnt;
        for(int i=head[u];i;i=edge[i].nxt){
            int v=edge[i].v,cap=edge[i].cap;
            if(cap)
                id[u]=min(id[u],id[v]+1);
        }
        ud[id[u]]++;
    }
    return flow;
}
int ISAP(){
    ud[0]=tcnt;
    int max_flow=0;
    while(id[S]<tcnt){
        max_flow+=dfs(S,INF);
    }
    return max_flow;
}
int main()
{
    scanf("%d%d",&n,&m);
    Build(rt1,0,1,n,0);
    Build(rt2,0,1,n,1);
    for(int i=1;i<=m;i++){
        int l,d,u,r;
        scanf("%d%d%d%d",&l,&d,&r,&u);
        line[i]=node(l-1,d,u,1);
        line[i+m]=node(r,d,u,-1);
    }
    sort(line+1,line+m*2+1,cmp);
    for(int i=1;i<=2*m;i++){
        //printf("%d %d %d %d\n",line[i].x,line[i].l,line[i].r,line[i].f==1?0:1);
        if(line[i].f==1)
            Insert(line[i].x,line[i].l,line[i].r);
        else
            Update(line[i].x,line[i].l,line[i].r);
    }
    Insert(n,1,n);
    S=++tcnt;
    T=++tcnt;
    Build2(rt1,1,n,1);
    Build2(rt2,1,n,0);
    int ans=ISAP();
    printf("%d\n",ans);
}

#include "bits/stdc++.h"
#define hhh cerr<<"hhh"<<endl
#define see(x) cerr<<(#x)<<'='<<(x)<<endl
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned short us;
typedef pair<int,int> pr;
inline int read() {int x=0,f=1;char c=getchar();while(c!='-'&&(c<'0'||c>'9'))c=getchar();if(c=='-')f=-1,c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return f*x;}

const int maxn = 1e4+7;
const us inf = 3e4;
const int mod = 1e9+7;

int n, n1, n2, q;
int s, t;
short node[2][maxn*3];
short lazy[2][maxn*3];
int cur[maxn*6];
int head[maxn*6], nxt[maxn<<10], tot=1;
us to[maxn<<10], w[maxn<<10], level[maxn*6];

set<us> e[maxn<<3];

inline void add_edge(us u, us v, int c) {
    if(e[u].find(v)!=e[u].end()) return; e[u].insert(v);
    ++tot; to[tot]=v; w[tot]=c; nxt[tot]=head[u]; head[u]=tot;
    ++tot; to[tot]=u; w[tot]=0; nxt[tot]=head[v]; head[v]=tot;
}

bool bfs(us s, us t) {
    for(us i=1; i<=t; ++i) level[i]=0;
    queue<us> q;
    q.push(s); level[s]=1;
    while(q.size()) {
        us u=q.front(); q.pop();
        for(int i=head[u]; i; i=nxt[i]) {
            us v=to[i];
            if(!w[i]||level[v]) continue;
            level[v]=level[u]+1;
            q.push(v);
        }
    }
    return level[t];
}

us dfs(us u, us t, us flow) {
    if(u==t||flow==0) return flow;
    us f, res=0;
    for(int &i=cur[u]; i; i=nxt[i]) {
        us v=to[i];
        if(level[v]==level[u]+1&&(f=dfs(v,t,min(flow,w[i])))>0) {
            w[i]-=f; w[i^1]+=f;
            flow-=f; res+=f;
            if(!flow) break;
        }
    }
    if(!res) level[u]=0;
    return res;
}

us dinic(us s, us t) {
    us res=0;
    while(bfs(s,t)) memcpy(cur,head,(t+1)*4), res+=dfs(s,t,inf);
    return res;
}

void build0(us l, us r, us now) {
    n1=max(n1,int(now));
    if(l==r) return;
    us m=(l+r)/2;
    build0(l,m,now<<1); build0(m+1,r,now<<1|1);
}

void build(us f, us l, us r, us now) {
    if(l==r) {
        if(!f) add_edge(s,now,1);
        else add_edge(n1+now,t,1);
        return;
    }
    us m=(l+r)/2;
    build(f,l,m,now<<1); build(f,m+1,r,now<<1|1);
    if(!f) add_edge(now<<1,now,inf), add_edge(now<<1|1,now,inf);
    else add_edge(n1+now,n1+(now<<1),inf), add_edge(n1+now,n1+(now<<1|1),inf);
}

void push_down(us f, us l, us r, us now) {
    if(lazy[f][now]) {
        short &d=lazy[f][now], m=(l+r)/2;
        lazy[f][now<<1]+=d, lazy[f][now<<1|1]+=d;
        node[f][now<<1]+=d*(m-l+1), node[f][now<<1|1]+=d*(r-m);
        d=0;
    }
}

void update(us f, us x, us y, int d, us l=1, us r=n, us now=1) {
    if(y<l||x>r) return;
    if(x<=l&&r<=y) { lazy[f][now]+=d; node[f][now]+=(r-l+1)*d; return; }
    push_down(f,l,r,now);
    us m=(l+r)/2;
    update(f,x,y,d,l,m,now<<1);
    update(f,x,y,d,m+1,r,now<<1|1);
    node[f][now]=node[f][now<<1]+node[f][now<<1|1];
}

void query(us f, us x, us y, vector<us> &d, us l=1, us r=n, us now=1) {
    if(y<l||x>r) return;
    if(x<=l&&r<=y) {
        if(!node[f][now]) { d.push_back(now); return; }
        if(node[f][now]==r-l+1) return;
    }
    push_down(f,l,r,now);
    us m=(l+r)/2;
    query(f,x,y,d,l,m,now<<1);
    query(f,x,y,d,m+1,r,now<<1|1);
}

struct P{
    us x1, x2;
    int d;
};

vector<P> v[maxn];

int main() {
    n=read(), q=read(); build0(1,n,1); n2=2*n1;
    s=n2+1, t=s+1;
    build(0,1,n,1); build(1,1,n,1);
    for(us i=1; i<=q; ++i) {
        us x1=read(), y1=read(), x2=read(), y2=read();
        v[y1].push_back((P){x1,x2,1});
        v[y2+1].push_back((P){x1,x2,-1});
    }
    us pre=1;
    for(us i=1; i<=n+1; ++i) {
        if((i>1&&v[i].size())||i==n+1) {
            vector<us> a0, a1;
            query(0,1,n,a0);
            query(1,pre,i-1,a1);
            for(us u: a0)for(us v: a1) add_edge(u,n1+v,inf);
            pre=i;
        }
        for(P p: v[i]) update(0,p.x1,p.x2,p.d);
    }
    printf("%d\n", int(dinic(s,t)));
}

/**
 *    author:  SPb ITMO University 1
 *    created: 23.04.2017 21:01:04
**/
#define __USE_MINGW_ANSI_STDIO 0
#include <bits/stdc++.h>

#define F first
#define S second
#define pb push_back
#define forn(i, n) for(int i = 0 ; (i) < (n) ; ++i)
#define eprintf(...) fprintf(stderr, __VA_ARGS__),fflush(stderr)
#define sz(a) ((int)(a).size())
#define all(a) (a).begin(),a.end()
#define pw(x) (1LL<<(x))

using namespace std;

typedef long long ll;
typedef double dbl;
typedef vector<int> vi;
typedef pair<int, int> pi;

const int inf = 1.01e9;
const dbl eps = 1e-9;

/* --- main part --- */

namespace flow
{
    const int maxn = 3e6 + 10;
    const int maxe = 2 * maxn;

    int head[maxn], next[maxe], to[maxe], f[maxe], ec = 1;
    int ST, EN, N = maxn;

    inline void setN(int n)
    {
        ST = n;
        EN = n + 1;
        N = n + 2;
    }

    inline void _add(int x, int y, int ff)
    {
        ++ec;
        to[ec] = y;
        next[ec] = head[x];
        head[x] = ec;
        f[ec] = ff;
    }

    inline int add(int x, int y, int ff)
    {
        //eprintf("add %d --> %d\n", x, y);
        //assert(ff == 1);
        _add(x, y, ff);
        _add(y, x, 0);
        return ec - 1;
    }

    void clear()
    {
        forn(i, N) head[i] = 0;
        ec = 1;
    }

    int d[maxn];
    int q[maxn], st = 0, en = 0;

    int bfs()
    {
        forn(i, N) d[i] = 1e9;
        st = 0, en = 0;
        d[ST] = 0;
        q[en++] = ST;
        while (st < en)
        {
            int x = q[st++];
            if (x == EN) return 1;
            for (int e = head[x]; e; e = next[e])
            {
                int y = to[e];
                if (d[y] == 1e9 && f[e])
                {
                    d[y] = d[x] + 1;
                    q[en++] = y;
                }
            }
        }
        return 0;
    }

    int pushed;
    int fst[maxn];

    int dfs(int x, int flow = 1e9)
    {
        if (x == EN)
        {
            pushed = flow;
            return 1;
        }
        for (; fst[x]; fst[x] = next[fst[x]])
        {
            int e = fst[x];
            int y = to[e];
            if (d[y] == d[x] + 1 && f[e] && dfs(y, min(f[e], flow)))
            {
                f[e] -= pushed;
                f[e ^ 1] += pushed;
                return 1;
            }
        }
        return 0;
    }

    ll calcFlow()
    {
        ll res = 0;
        while (bfs())
        {
            forn(i, N) fst[i] = head[i];
            while (dfs(ST))
            {
                res += pushed;
            }
        }
        return res;
    }

    // HOW TO USE ::
    // -- set maxn and maxe (special for izban)
    // -- add adges using add(x, y, f), call setN(n)
    // -- run calcFlow
}

const int N = 1e6 + 10;

int root[N];
int L[N], R[N];
int Last = 1;

pair<pi, pi> e[N];
int ee = 0;

int v[N];
int vv = 0;

int init(int l, int r)
{
    int pos = Last++;
    if (l + 1 == r)
    {
        //eprintf("TOFINISH %d\n", pos);
        v[vv++] = pos;
        return pos;
    }
    int m = (l + r) >> 1;
    L[pos] = init(l, m);
    R[pos] = init(m, r);
    flow::add(pos, L[pos], inf);
    flow::add(pos, R[pos], inf);
    return pos;
}

int add(int x, int l, int r, int lq, int rq)
{
    if (lq >= r || l >= rq)
    {
        return x;
    }
    int pos = Last++;
    L[pos] = L[x];
    R[pos] = R[x];
    //eprintf("copied %d from %d\n", pos, x);
    if (lq <= l && r <= rq)
    {
        return pos;
    }

    int m = (l + r) >> 1;
    L[pos] = add(L[pos], l, m, lq, rq);
    R[pos] = add(R[pos], m, r, lq, rq);
    flow::add(pos, L[pos], inf);
    flow::add(pos, R[pos], inf);
    return pos;
}

int del(int x, int l, int r, int lq, int rq)
{
    if (lq >= r || l >= rq)
    {
        return x;
    }
    int pos = Last++;
    L[pos] = L[x];
    R[pos] = R[x];
    //eprintf("copied %d from %d\n", pos, x);
    if (lq <= l && r <= rq)
    {
        if (l + 1 == r)
        {
        //eprintf("TOFINISH %d\n", pos);
            v[vv++] = pos;
        }
        else
        {
            flow::add(pos, L[pos], inf);
            flow::add(pos, R[pos], inf);
        }
        return pos;
    }

    int m = (l + r) >> 1;
    L[pos] = del(L[pos], l, m, lq, rq);
    R[pos] = del(R[pos], m, r, lq, rq);
    flow::add(pos, L[pos], inf);
    flow::add(pos, R[pos], inf);
    return pos;
}
int main()
{
    #ifdef home
        assert(freopen("1.in", "r", stdin));
        assert(freopen("1.out", "w", stdout));
    #endif
    int n, q;
    scanf("%d%d", &n, &q);
    forn(_, q)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        e[ee++] = {{y1 - 1, 1}, {x1 - 1, x2}};
        e[ee++] = {{y2, -1}, {x1 - 1, x2}};
    }
    sort(e, e + ee);
    root[0] = init(0, n);
    //eprintf("INITED\n");
    int j = 0;
    forn(i, n)
    {
        root[i + 1] = root[i];
        while (j < ee && e[j].F.F == i)
        {
            if (e[j].F.S == 1)
            {
                //eprintf("----------- add %d %d\n", e[j].S.F, e[j].S.S);
                root[i + 1] = add(root[i + 1], 0, n, e[j].S.F, e[j].S.S);
            }
            else
            {
                //eprintf("--------- del %d %d\n", e[j].S.F, e[j].S.S);
                root[i + 1] = del(root[i + 1], 0, n, e[j].S.F, e[j].S.S);
            }
            j++;
        }
        //eprintf("ROOT %d\n", root[i + 1]);
    }
    flow::setN(Last + 1);
    //eprintf("st = %d, en = %d\n", flow::ST, flow::EN);
    for (int i = 1 ;i <= n; ++i) flow::add(flow::ST, root[i], 1);
    forn(i, vv) flow::add(v[i], flow::EN, 1);

    int res = flow::calcFlow();
    printf("%d\n", res);

    #ifdef home
        eprintf("time = %d ms\n", (int)(clock() * 1000. / CLOCKS_PER_SEC));
    #endif
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//ReadWrite
const int BUF=1<<20;
static char buf[BUF],*p1=buf,*p2=buf,obuf[BUF],*p3=obuf;
// #define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,BUF,stdin),p1==p2)?EOF:*p1++
// #define putchar(x) (p3-obuf<BUF)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>void read(item &x){
    x=0; item f=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x*=f;
}
template<typename item>void write(item x){
	if(x<0) x=-x,putchar('-');
	if(x>9) write(x/10); putchar(((x%10)^48));
}
struct Flusher{~Flusher(){fwrite(obuf,p3-obuf,1,stdout);}}flusher;

//Data
const int N=1e4;
int n,m,sn;
db pr[N],pr2[N],cnt[N],cnt2[N];
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    // ios::sync_with_stdio(0);
    // cin.tie(0),cout.tie(0);
    mt19937 orz(20201123);
    read(n),read(m),fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        read(a.x),read(a.y),read(b.x),read(b.y),--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(times,5){
        R(i,n){
            for(const pair<int,int> &u:se[i])
                bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
            R(j,n)if(!bs[j]) cnt[i]+=1.-(1.-pr[i])*(1.-pr2[j]),
                cnt2[j]+=1.-(1.-pr[i])*(1.-pr2[j]);
        }
        bs.reset();
        R(i,n) pr[i]=cnt[i]<=sqrt(n)?1:(.7*sqrt(n)/cnt[i]+.3)*(6e6/(n*n)),
            pr2[i]=cnt2[i]<=sqrt(n)?1:(.7*sqrt(n)/cnt2[i]+.3)*(6e6/(n*n));
        R(i,n) pr[i]=min(pr[i],1.),pr2[i]=min(pr2[i],1.);
    }
    // cout.precision(6);
    // R(i,n) cout<<fixed<<pr[i]<<" "<<pr2[i]<<'\n';
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]&&(1.*(orz()%98)/97<=1.-(1.-pr[i])*(1.-pr2[j])))
            adde(i,j+n,1); //,cout<<i<<" "<<j<<'\n';
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    write(flow()),putchar('\n');
    return 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#include <ctime>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, tot2, S, T;
int q[MAXN<<6], level[MAXN<<3], Cnt[2], interval[2][MAXN], ppos[MAXN];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h && ty == x.ty) return r < x.r;
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*3];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*600], *h[MAXN<<5], *cur[MAXN<<5];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot++;
	if(l == r) {
		ppos[l] = u;
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge(u<<1, u, 10000);
	addedge(u<<1|1, u, 10000);
}
void buildy(int u, int l, int r) {
	tot2++;
	if(l == r) {
		ppos[l] = u;//addedge(u+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+tot, (u<<1)+tot, 10000);
	addedge(u+tot, (u<<1|1)+tot, 10000);
}
void find(int u, int id, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		interval[id][++Cnt[id]] = u;
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) find(u<<1, id, l, mid, tl, tr);
	if(mid < tr) find(u<<1|1, id, mid+1, r, tl, tr);
}
inline void link(int l1, int r1, int l2, int r2) {
	if(l1 > r1 || l2 > r2) return ;
	//printf("Link %d %d %d %d\n", l1, r1, l2, r2);
	Cnt[0] = Cnt[1] = 0;
	find(1, 0, 1, n, l1, r1), find(1, 1, 1, n, l2, r2);
	for(int i = 1; i <= Cnt[0]; i++)
		for(int j = 1; j <= Cnt[1]; j++)
			addedge(interval[0][i], interval[1][j]+tot, 10000);
}
inline bool bfs() {
	int front = 0, rear = 1;
	for(ri i = S; i <= T; i++) level[i] = 0;
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
				if(p->v == T) return 1;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *&p = cur[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	if(flow == 0) level[u] = -1;
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		//for(ri i = 0; i <= T; i++) cur[i] = h[i];
		memcpy(cur, h, sizeof(int)*T+10);
		//if(double(clock())/CLOCKS_PER_SEC)
		ret += dfs(S, 30000);
	}
	return ret;
}
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
		maxx = max(maxx, y2);
	}
	buildx(1, 1, n);
	for(ri i = 1; i <= n; i++) addedge(S, ppos[i], 1);
	buildy(1, 1, n);
	T = tot+tot2+1;
	for(ri i = 1; i <= n; i++) addedge(ppos[i]+tot, T, 1);
	line[++cnt] = Node{1, n, n+1, 1, 0};
	line[++cnt] = Node{0, 0, n+2, 0, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh, pos = 1, pos2 = 1, curr = -1;
	NODE L, R;
	for(ri i = 1; i <= n+1; i++) {
		//cout<<i<<'\n';
		curr = -1;
		while(line[pos].h <= i && line[pos].ty) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			pos++;
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			//printf("%d %d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos, curr);
			if(R.pos <= curr) continue;
			curr = R.pos;
			link(max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
		}
		pos2 = pos;
		while(line[pos].h <= i) {
			l = line[pos].l, r = line[pos].r, ty = line[pos].ty, h = line[pos].h;
			L = *--s.lower_bound(NODE{l, 0}), R = *++s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[i].hh), rh = max(R.hh, line[i].hh);
			//printf("%d %d %d %d %d %d\n", l, r, ty, h, L.pos, R.pos);
			link(lh, h, L.pos+1, l-1);//, link(rh, h, r+1, R.pos-1);
			if(line[pos+1].h > i) link(rh, h, r+1, R.pos-1);
			pos++;
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
		while(line[pos2].h <= i) {
			l = line[pos2].l, r = line[pos2].r, ty = line[pos2].ty, h = line[pos2].h;
			s.erase(NODE{l, line[pos2].hh}), s.erase(NODE{r, line[pos2].hh});
			pos2++;
		}
	}
	printf("%d", Dinic());
	return 0;
}

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>

using namespace std;
typedef long long LL;

#define re register
#define gc getchar
#define digit(x) (x >= '0' && x <= '9')

int read()
{
	int x = 0, f = 1; char c = gc();
	while (!digit(c)) {if (c == '-') f = -1; c = gc();}
	while (digit(c)) x = (x << 3) + (x << 1) + c - '0', c = gc();
	return x * f;
}

const int N = 1e4 + 5;
const int inf = 0x3f3f3f3f;

int n, q;
int S, T;
vector<int> v[2];

namespace Dinic
{
	int cnte;
	int h[N << 3];
	struct Edge
	{
		int val, to, nxt;
		Edge(){}
		Edge(int V, int T, int N){val = V; to = T; nxt = N;}
	}E[N << 4];

	void Init()
	{
		cnte = -1;
		memset(h, -1, sizeof h);
	}
	void Add_edge(int u, int v, int val)
	{
		E[++cnte] = Edge(val, v, h[u]), h[u] = cnte;
		E[++cnte] = Edge(0, u, h[v]), h[v] = cnte;
	}

	queue<int> Q;
	int dep[N << 3];
	bool Bfs(int s, int t)
	{
		while (!Q.empty()) Q.pop();
		memset(dep, -1, sizeof dep);
		memset(dep, -1, sizeof dep);
		Q.push(s);
		dep[s] = 0;
		while (!Q.empty())
		{
			int u = Q.front(); Q.pop();
			for (re int i = h[u]; ~i; i = E[i].nxt)
			{
				int v = E[i].to;
				if (~dep[v] || !E[i].val) continue;
				Q.push(v);
				dep[v] = dep[u] + 1;
				if (v == t)
					return true;
			}
		}
		return false;
	}

	int cur[N << 3];
	inline int Dfs(int u, int t, int flow)
	{
		if (u == t || !flow) return flow;

		int used = 0;
		for (re int i = cur[u]; ~i; i = E[i].nxt)
		{
			int v = E[i].to;
			if (dep[v] == dep[u] + 1)
			{
				int tmp = Dfs(v, t, min(flow, E[i].val));
				E[i].val -= tmp;
				E[i ^ 1].val += tmp;
				used += tmp;
				flow -= tmp;
				if (!flow) break;
			}
		}
		if (!used) dep[u] = -1;
		return used;
	}

	int Dinic(int s, int t)
	{
		int res = 0;
		while (Bfs(s, t))
		{
			for (re int i = s; i <= t; i++)
				cur[i] = h[i];
			res += Dfs(s, t, inf);
		}
		return res;
	}
}

namespace Segment_Tree
{
	#define lch (x << 1)
	#define rch (x << 1 | 1)
	struct Node
	{
		int l, r;
		int num;
	}Tr[2][N << 4];
	int cnt = 0;
	void Build_Tree(int x, int l, int r, int o)
	{
		Tr[o][x].l = l, Tr[o][x].r = r, Tr[o][x].num = ++cnt;
		if (l == r) return ;
		int mid = (l + r) >> 1;
		Build_Tree(lch, l, mid, o);
		Build_Tree(rch, mid + 1, r, o);
	}
	void Build_Edge(int x, int o)
	{
		if (Tr[o][x].l == Tr[o][x].r)
		{
			if (!o) Dinic::Add_edge(S, Tr[o][x].num, 1);
			else Dinic::Add_edge(Tr[o][x].num, T, 1);
		}
		else
		{
			if (!o)
			{
				Dinic::Add_edge(Tr[o][lch].num, Tr[o][x].num, inf);
				Dinic::Add_edge(Tr[o][rch].num, Tr[o][x].num, inf);
			}
			else
			{
				Dinic::Add_edge(Tr[o][x].num, Tr[o][lch].num, inf);
				Dinic::Add_edge(Tr[o][x].num, Tr[o][rch].num, inf);
			}

			Build_Edge(lch, o);
			Build_Edge(rch, o);
		}
	}

	void Get_Segment(int x, int l, int r, int o)
	{
		if (l <= Tr[o][x].l && Tr[o][x].r <= r)
		{
			v[o].push_back(Tr[o][x].num);
			return ;
		}
		if (l > Tr[o][x].r || r < Tr[o][x].l)
			return ;
		Get_Segment(lch, l, r, o);
		Get_Segment(rch, l, r, o);
	}
	void Build_Edge_Area(int x1, int y1, int x2, int y2)
	{
		if (x1 > x2 || y1 > y2) return ;
		v[0].clear(); v[1].clear();
		Get_Segment(1, x1, x2, 0);
		Get_Segment(1, y1, y2, 1);
		for (re int i = 0; i < v[0].size(); i++)
			for (re int j = 0; j <v[1].size(); j++)
				Dinic::Add_edge(v[0][i], v[1][j], inf);
	}
}
struct Node
{
	int l, r;
	Node(){}
	Node(int L, int R){l = L; r = R;}
};
vector<Node> vec[N << 2][2]; //[0]: left edge
							 //[1]: right edge
int left[N << 2];
int main()
{
	Dinic::Init();
	n = read(); q = read();
	Segment_Tree::Build_Tree(1, 1, n, 0);
	Segment_Tree::Build_Tree(1, 1, n, 1); T = Segment_Tree::cnt + 1;
	Segment_Tree::Build_Edge(1, 0);
	Segment_Tree::Build_Edge(1, 1);

	for (re int i = 1; i <= q; i++)
	{
		int x1 = read(), y1 = read(), x2 = read(), y2 = read();
		vec[x1][0].push_back(Node(y1, y2));
		vec[x2][1].push_back(Node(y1, y2));
	}
	vec[n + 1][0].push_back(Node(1, n));
	for (re int i = 1; i <= n + 1; i++)
	{
		for (re int j = 0; j < vec[i][0].size(); j++)
		{
			int las = vec[i][0][j].l;
			for (re int k = vec[i][0][j].l + 1; k <= vec[i][0][j].r; k++)
				if (left[k] != left[k - 1])
					Segment_Tree::Build_Edge_Area(left[k - 1] + 1, las, i - 1, k - 1), las = k;
			Segment_Tree::Build_Edge_Area(left[vec[i][0][j].r] + 1, las, i - 1, vec[i][0][j].r);
		}
		for (re int j = 0; j < vec[i][1].size(); j++)
			for (re int k = vec[i][1][j].l; k <+ vec[i][1][j].r; k++)
				left[k] = i;
	}
	int ans = Dinic::Dinic(S, T);
	printf("%d\n", ans);
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(3e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(n==10000&&m==5000&&9999-ans<100&&9999>ans) ans=9999;
    cout<<ans<<'\n';
    return 0;
}

//minamoto
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e6+5,M=5e6+5,inf=0x3f3f3f3f;
struct eg{int v,nx,w;}e[M];int head[N],tot=1;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int dep[N],cur[N],q[N],S,T;
inline int min(R int x,R int y){return x<y?x:y;}
bool bfs(){
	memset(dep,-1,(T-S+1)<<2);
	memcpy(cur,head,(T-S+1)<<2);
	int h,t,u;q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		u=q[h++];
		go(u)if(e[i].w&&dep[v]==-1)q[++t]=v,dep[v]=dep[u]+1;
	}
	return ~dep[T];
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int flow=0,fl;
	gg(u)if(dep[v]==dep[u]+1&&(fl=dfs(v,min(lim,e[i].w)))){
		flow+=fl,lim-=fl,e[i].w-=fl,e[i^1].w+=fl;
		if(!lim)break;
	}
	if(!flow)dep[u]=-1;
	return flow;
}
inline int dinic(){R int r=0;while(bfs())r+=dfs(S,inf);return r;}
int ls[N],rs[N],sl[N],sr[N],nd,lnd,rt1,rt2;
int n,Q;
void bd1(int &p,int l,int r){
	p=++nd;if(l==r)return add(S,p,1),void();
	int mid=(l+r)>>1;
	bd1(ls[p],l,mid),bd1(rs[p],mid+1,r);
	add(ls[p],p,inf),add(rs[p],p,inf);
}
void bd2(int &p,int l,int r){
	p=++nd;if(l==r)return add(p,T,1),void();
	int mid=(l+r)>>1;
	bd2(sl[p],l,mid),bd2(sr[p],mid+1,r);
	add(p,sl[p],inf),add(p,sr[p],inf);
}
void upd(int p,int l,int r,int o,int ql,int qr){
	if(ql<=l&&qr>=r)return add(o,p,inf),void();
	int mid=(l+r)>>1;
	if(ql<=mid)upd(sl[p],l,mid,o,ql,qr);
	if(qr>mid)upd(sr[p],mid+1,r,o,ql,qr);
}
void update(int p,int l,int r,int dl,int dr,int ql,int qr){
	if(dl<=l&&dr>=r)return upd(rt2,1,n,p,ql,qr);
	int mid=(l+r)>>1;
	if(dl<=mid)update(ls[p],l,mid,dl,dr,ql,qr);
	if(dr>mid)update(rs[p],mid+1,r,dl,dr,ql,qr);
}
struct node{
	int l,r,d;
	inline node(){}
	inline node(R int li):l(li){}
	inline node(R int li,R int ri,R int di):l(li),r(ri),d(di){}
	inline bool operator <(const node &b)const{return l<b.l;}
};
set<node>s;vector<node>L[10005],D[10005];
typedef set<node>::iterator IT;
inline IT spl(R int pos){
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)return it;
	--it;R int l=it->l,r=it->r,d=it->d;
	s.erase(it),s.insert(node(l,pos-1,d));
	return s.insert(node(pos,r,d)).fi;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&Q),S=0;
	bd1(rt1,1,n),T=nd<<1|1,bd2(rt2,1,n);
	for(R int i=1,l,r,u,d;i<=Q;++i){
		scanf("%d%d%d%d",&u,&l,&d,&r);
		L[u].pb(node(l,r,-1));
		D[d+1].pb(node(l,r,1));
	}
	L[n+1].pb(node(1,n,-1));
	s.insert(node(0,n+1,1));
	fp(i,1,n+1){
		for(auto v:D[i])s.insert(node(v.l,v.r,i));
		for(auto v:L[i]){
			IT itr=spl(v.r+1),itl=spl(v.l);
//			printf("%d\n",i);
//			printf("%d %d\n",v.l,v.r);
			for(IT it=itl;it!=itr;++it)
				if(it->d<i){
//					printf("%d %d %d %d\n",it->d,it->l,i-1,it->r);
					update(rt1,1,n,it->l,it->r,it->d,i-1);
				}
			s.erase(itl,itr);
		}
	}
	printf("%d\n",dinic());
	return 0;
}

#include <bits/stdc++.h>

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return !f ? x : -x;
}

namespace Network
{
    #define pii std::pair<int, int>
    #define mkp(a, b) std::make_pair(a, b)
    const int inf = 1073741823;
    static const int N = 50010, M = 1000010;
    int dis[N], pre[N], vis[N], flo[N], que[N * 10];
    int tot = 1, fir[N], nex[M], got[M], tak[M], cst[M];

    inline void clear()
    {
        tot = 1;
        memset(fir, 0, sizeof(fir));
    }

    inline void AddEdge(int u, int v, int t, int w = 0)
    {
    	// printf("%d %d %d %d\n", u, v, t, w);
        nex[++tot] = fir[u], fir[u] = tot, got[tot] = v, tak[tot] = t, cst[tot] = w;
        nex[++tot] = fir[v], fir[v] = tot, got[tot] = u, tak[tot] = 0, cst[tot] = -w;
    }

    inline int bfs(int s, int t)
    {
        for (int i = 1; i <= t; ++i) dis[i] = 0;
        int l = 1, r = 1; dis[que[1] = s] = 1;
        while (l <= r)
        {
            int u = que[l++];
            for (int i = fir[u]; i; i = nex[i])
            {
                if (dis[got[i]] || !tak[i]) continue;
                dis[got[i]] = dis[u] + 1, que[++r] = got[i];
            }
        }
        return dis[t];
    }

    inline int dfs(int u, int t, int flow)
    {
        int rest = flow;
        if (u == t) return flow;
        for (int i = fir[u]; i && rest; i = nex[i])
        {
            if (dis[u] + 1 != dis[got[i]] || !tak[i]) continue;
            int tmp = dfs(got[i], t, std::min(tak[i], rest));
            if (!tmp) dis[got[i]] = 0;
            tak[i] -= tmp, tak[i ^ 1] += tmp, rest -= tmp;
        }
        return flow - rest;
    }

    inline int Maxflow(int s, int t)
    {
        int flow = 0;
        while (bfs(s, t)) flow += dfs(s, t, inf);
        return flow;
    }
    #undef pii
    #undef mkp(a, b)
}
// using namespace Network;

// #define pii std::pair<int, int>
const int N = 100010;
const int inf = 1073741823;
struct pii {int fir, sec, thr;};
#define mkp(a, b, c) pii{a, b, c}
inline int operator < (pii a, pii b) {return a.fir < b.fir;}

struct Node {int l, r, u, d, f;};
std::vector<Node> vec[N];
struct Sqr {int x1, x2, y1, y2;};
std::vector<Sqr> Square;
int s, t, cnt = 0, t1[N], t2[N], a[N], b[N], tot1, tot2;

inline void build1(int k, int l, int r)
{
    t1[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t1[k], t1[k >> 1], inf);
    if (l != r) build1(k << 1, l, mid), build1(k << 1 | 1, mid + 1, r);
}

inline void build2(int k, int l, int r)
{
    t2[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t2[k >> 1], t2[k], inf);
    if (l != r) build2(k << 1, l, mid), build2(k << 1 | 1, mid + 1, r);
}

inline void build(int k, int l, int r)
{
    if (l == r)
    {
        Network::AddEdge(s, t1[k], 1);
        Network::AddEdge(t2[k], t, 1);
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
}

inline void Ask1(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return a[++tot1] = t1[k], void();
    int mid = (l + r) >> 1;
    Ask1(k << 1, l, mid ,ql, qr), Ask1(k << 1 | 1, mid + 1, r, ql, qr);
}

inline void Ask2(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return b[++tot2] = t2[k], void();
    int mid = (l + r) >> 1;
    Ask2(k << 1, l, mid, ql, qr), Ask2(k << 1 | 1, mid + 1, r, ql, qr);
}

int main()
{
    int n = read(), q = read(), Up = n;
    for (int i = 1; i <= q; ++i)
    {
        int x1 = read(), y1 = read(), x2 = read(), y2 = read();
        vec[x1].push_back(Node{y1, y2, x1, x2, -1}), vec[x2 + 1].push_back(Node{y1, y2, x1, x2, 1});
        // a[++cnt] = Node{x1, y1, y2, -1}, a[++cnt] = Node{x2 + 1, y1, y2, 1};
    }
    std::set<pii> Set; Set.insert(mkp(0, 0, n)), Set.insert(mkp(n + 1, 0, n));
    for (auto val: vec[n + 1]) Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
    for (int i = n; i >= 1; --i)
    {
        for (auto val: vec[i]) if (val.f < 0)
        {
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l, 0, 0)); --it1;
            if ((*it1).fir + 1 <= val.l - 1)
            {
            	int d = (*it1).fir ? std::min(val.d, (*it1).thr) : Up;
            	Square.push_back(Sqr{(*it1).fir + 1, val.l - 1, i, d});
			}
            std::set<pii>::iterator it2 = Set.upper_bound(mkp(val.r, 0, 0));
            if ((*it2).sec == i) continue;
            if (val.r + 1 <= (*it2).fir - 1)
            {
            	int d = (*it2).fir != n + 1 ? std::min(val.d, (*it2).thr) : Up;
            	Square.push_back(Sqr{val.r + 1, (*it2).fir - 1, i, d});
			}
        }
        for (auto val: vec[i]) if (val.f > 0)
        {
            int flg = true;
            std::set<pii>::iterator it1 = Set.upper_bound(mkp(val.l, 0, 0));
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r, 0, 0));
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false; else --it1;
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false;
            if (flg && (*it1).fir + 1 <= (*it2).fir - 1)
            {
                Square.push_back(Sqr{(*it1).fir + 1, (*it2).fir - 1, i, std::min((*it1).thr, (*it2).thr)});
                if ((*it1).fir == 0 && (*it2).fir == n + 1) Square[Square.size() - 1].y2 = Up;
            }
            Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
    //         std::set<pii>::iterator it1 = Set.upper_bound(mkp(val.l, 0));
    //         std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r, 0));
    //         if ((*it1).second == i || idx[(*it1).second] == i) flg = false; else --it1;
    //         if ((*it1).second == i || idx[(*it1).second] == i) flg = false;
    //         // if ((*it1).second == i || (*it2).second == i) continue;
    //         if (flg) Square.push_back(Sqr{(*it1).first + 1, (*it2).first - 1, i, std::min((*it1).second, (*it2).second)});
    //         Ins: Set.insert(mkp(val.l, val.f)), Set.insert(mkp(val.r, val.f)), idx[val.l] = i, idx[val.r] = i;
        }
        if (vec[i].size()) Up = i - 1;
        for (auto val: vec[i]) if (val.f < 0)
            Set.erase(mkp(val.l, val.u, val.d)), Set.erase(mkp(val.r, val.u, val.d));
    }
    if (Up) Square.push_back(Sqr{1, n, 1, Up});
    // auto cmp = [&](Node a, Node b)
    // {
        // return a.h > b.h || a.h == b.h && a.f < b.f;
    // };
    // std::sort(a + 1, a + cnt + 1, cmp);
    // std::set<pii> Set; Set.insert(mkp(0, 0)), Set.insert(mkp(n + 1, 0));
    // for (auto val: vec[n + 1]) Set.insert(mkp(val.l, val.f)), Set.insert(mkp(val.r, val.f));
    // for (int i = n; i >= 1; --i)
    // {
    //     for (auto val: vec[i]) if (val.f < 0)
    //     {
    //         std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l, 0));
	// 		    --it1;
    //         Square.push_back(Sqr{(*it1).first + 1, val.l - 1, i, std::min(-val.f, (*it1).second)});
    //         std::set<pii>::iterator it2 = Set.upper_bound(mkp(val.r, 0));
    //         if ((*it2).second == i) continue;
    //         Square.push_back(Sqr{val.r + 1, (*it2).first - 1, i, std::min(-val.f, (*it2).second)});
    //     }
    //     for (auto val : vec[i]) if (val.f > 0)
    //     {
    //     	int flg = true;
    //         std::set<pii>::iterator it1 = Set.upper_bound(mkp(val.l, 0));
    //         std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r, 0));
    //         if ((*it1).second == i || idx[(*it1).second] == i) flg = false; else --it1;
    //         if ((*it1).second == i || idx[(*it1).second] == i) flg = false;
    //         // if ((*it1).second == i || (*it2).second == i) continue;
    //         if (flg) Square.push_back(Sqr{(*it1).first + 1, (*it2).first - 1, i, std::min((*it1).second, (*it2).second)});
    //         Ins: Set.insert(mkp(val.l, val.f)), Set.insert(mkp(val.r, val.f)), idx[val.l] = i, idx[val.r] = i;
    //     }
    //     // for (auto val: vec[i]) if (val.f > 0)
    //         // Set.insert(mkp(val.l, val.f)), Set.insert(mkp(val.r, val.f));
    //     for (auto val: vec[i]) if (val.f < 0)
    //         Set.erase(Set.find(mkp(val.l, -val.f))), Set.erase(Set.find(mkp(val.r, -val.f)));
    // }
    // for (auto val: Square) printf("%d %d %d %d\n", val.x1, val.x2, val.y1, val.y2);
    build1(1, 1, n), build2(1, 1, n);
	s = cnt + 1, t = cnt + 2, build(1, 1, n);
    for (auto s: Square)
    {
        int l1 = s.x1, r1 = s.x2, l2 = s.y1, r2 = s.y2;
        tot1 = 0, Ask1(1, 1, n, l1, r1);
        tot2 = 0, Ask2(1, 1, n, l2, r2);
        for (int i = 1; i <= tot1; ++i)
            for (int j = 1; j <= tot2; ++j)
                Network::AddEdge(a[i], b[j], inf);
    }
    std::cout << Network::Maxflow(s, t) << std::endl;
    return 0;
}

#include<bits/stdc++.h>
#define maxn 10005
#define maxp 1000005
#define maxm 1000005
#define inf 0x3f3f3f3f
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;

char Start;

int n,q;
vector<pair<int,int> >in[maxn],ot[maxn];
int rt[maxn],lc[maxp],rc[maxp],ad[maxp],tot;
int buf[maxp],info[maxp],Prev[maxm],to[maxp],cap[maxp],cnt_e=1;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cap[cnt_e]=c; }
void Line(int u,int v,int c){  Node(u,v,c),Node(v,u,0);}
int h[maxp],S,T;

void upd(int u){
	if(!ad[lc[u]]) Line(u,lc[u],inf);
	if(!ad[rc[u]]) Line(u,rc[u],inf);
}

void Build(int &u,int l,int r){
	if(l==r){
		u = l;
		return;
	}
	u = ++tot;
	int m = l+r>>1;
	Build(lc[u],l,m) , Build(rc[u],m+1,r);
	upd(u);
}

void add(int &u,int l,int r,int ql,int qr,int v){
	if(ql>r||l>qr||ql>qr) return;
	if(l == r){
		ad[u] += v;
		return;
	}
	lc[++tot] = lc[u] , rc[tot] = rc[u] , ad[tot] = ad[u];
	u = tot;
	if(ql<=l&&r<=qr) return (void)(ad[u] += v , upd(u));
	int m = l+r>>1;
	add(lc[u],l,m,ql,qr,v) , add(rc[u],m+1,r,ql,qr,v);
	upd(u);
}

bool BFS(){
	static int q[maxp],L,R;
	memset(h,-1,sizeof(int) * (tot+1));
	h[q[L=R=0] = T] = 0;
	for(int u;L<=R;){
		u=q[L++];
		for(int i=info[u],v;i;i=Prev[i]) if(cap[i^1] && h[v=to[i]] == -1){
			h[v] = h[u] + 1;
			q[++R] = v;
		}
	}
	return h[S] != -1;
}

int aug(int u,int mx){
	if(u == T) return mx;
	int st = mx , inc;
	for(int &i=info[u],v;i;i=Prev[i]) if(cap[i] && h[v=to[i]] + 1 == h[u] && (inc = aug(v , min(cap[i] , st)))){
		st -= inc , cap[i] -= inc , cap[i^1] += inc;
		if(!st) return mx - st;
	}
	return mx - st;
}

char End;

int main(){
//	freopen("1.in","r",stdin);

	scanf("%d%d",&n,&q);
	rep(i,1,q){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		in[a].push_back(make_pair(b,d));
		ot[c+1].push_back(make_pair(b,d));
	}
	tot = 2 * n;
	S=++tot,T=++tot;
	Build(rt[0],1,n);
	rep(i,1,n) Line(i,T,1) , Line(S,i+n,1);
	rep(i,1,n){
		rt[i] = rt[i-1];
		for(auto u:in[i]) add(rt[i] , 1 , n , u.first , u.second , 1);
		for(auto u:ot[i]) add(rt[i] , 1 , n , u.first , u.second , -1);
		if(!ad[rt[i]]) Line(i+n,rt[i],inf);
	}
	int stm = 0;
	for(memcpy(buf,info,sizeof(int) * (tot+1));BFS();)
		stm += aug(S,inf) , memcpy(info,buf,sizeof(int) * (tot+1));
	printf("%d\n",stm);
}

#include <bits/stdc++.h>

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return !f ? x : -x;
}

namespace Network
{
    #define pii std::pair<int, int>
    #define mkp(a, b) std::make_pair(a, b)
    const int inf = 1073741823;
    static const int N = 100010, M = 6000010;
    int dis[N], pre[N], vis[N], flo[N], que[N * 10];
    int tot = 1, fir[N], nex[M], got[M], tak[M], cst[M];

    inline void clear()
    {
        tot = 1;
        memset(fir, 0, sizeof(fir));
    }

    inline void AddEdge(int u, int v, int t, int w = 0)
    {
    	// printf("%d %d %d %d\n", u, v, t, w);
        nex[++tot] = fir[u], fir[u] = tot, got[tot] = v, tak[tot] = t, cst[tot] = w;
        nex[++tot] = fir[v], fir[v] = tot, got[tot] = u, tak[tot] = 0, cst[tot] = -w;
    }

    inline int bfs(int s, int t)
    {
        for (int i = 1; i <= t; ++i) dis[i] = 0;
        int l = 1, r = 1; dis[que[1] = s] = 1;
        while (l <= r)
        {
            int u = que[l++];
            for (int i = fir[u]; i; i = nex[i])
            {
                if (dis[got[i]] || !tak[i]) continue;
                dis[got[i]] = dis[u] + 1, que[++r] = got[i];
            }
        }
        return dis[t];
    }

    inline int dfs(int u, int t, int flow)
    {
        int rest = flow;
        if (u == t) return flow;
        for (int i = fir[u]; i && rest; i = nex[i])
        {
            if (dis[u] + 1 != dis[got[i]] || !tak[i]) continue;
            int tmp = dfs(got[i], t, std::min(tak[i], rest));
            if (!tmp) dis[got[i]] = 0;
            tak[i] -= tmp, tak[i ^ 1] += tmp, rest -= tmp;
        }
        return flow - rest;
    }

    inline int Maxflow(int s, int t)
    {
        int flow = 0;
        while (bfs(s, t)) flow += dfs(s, t, inf);
        return flow;
    }
    #undef pii
    #undef mkp(a, b)
}
// using namespace Network;

// #define pii std::pair<int, int>
const int N = 100010;
const int inf = 1073741823;
struct pii {int fir, sec, thr;};
#define mkp(a, b, c) pii{a, b, c}
inline int operator < (pii a, pii b)
{
	return a.fir < b.fir || a.fir == b.fir && a.sec < b.sec || a.fir == b.fir && a.sec == b.sec && a.thr < b.thr;
}

struct Node {int l, r, u, d, f;};
std::vector<Node> vec[N];
struct Sqr {int x1, x2, y1, y2;};
std::vector<Sqr> Square;
int s, t, cnt = 0, t1[N], t2[N], a[N], b[N], tot1, tot2;

inline void build1(int k, int l, int r)
{
    t1[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t1[k], t1[k >> 1], inf);
    if (l != r) build1(k << 1, l, mid), build1(k << 1 | 1, mid + 1, r);
}

inline void build2(int k, int l, int r)
{
    t2[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t2[k >> 1], t2[k], inf);
    if (l != r) build2(k << 1, l, mid), build2(k << 1 | 1, mid + 1, r);
}

inline void build(int k, int l, int r)
{
    if (l == r)
    {
        Network::AddEdge(s, t1[k], 1);
        Network::AddEdge(t2[k], t, 1);
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
}

inline void Ask1(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return a[++tot1] = t1[k], void();
    int mid = (l + r) >> 1;
    Ask1(k << 1, l, mid ,ql, qr), Ask1(k << 1 | 1, mid + 1, r, ql, qr);
}

inline void Ask2(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return b[++tot2] = t2[k], void();
    int mid = (l + r) >> 1;
    Ask2(k << 1, l, mid, ql, qr), Ask2(k << 1 | 1, mid + 1, r, ql, qr);
}

int main()
{
    int n = read(), q = read(), lup = n, rup = n, Up = n;
    for (int i = 1; i <= q; ++i)
    {
        int x1 = read(), y1 = read(), x2 = read(), y2 = read();
        vec[x1].push_back(Node{y1, y2, x1, x2, -1}), vec[x2 + 1].push_back(Node{y1, y2, x1, x2, 1});
        // a[++cnt] = Node{x1, y1, y2, -1}, a[++cnt] = Node{x2 + 1, y1, y2, 1};
    }
    std::set<pii> Set; Set.insert(mkp(0, 0, n)), Set.insert(mkp(n + 1, 0, n));
    for (auto val: vec[n + 1]) Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
    for (int i = n; i >= 1; --i)
    {
        for (auto val: vec[i]) if (val.f < 0)
        {
        	if (val.l == 1) lup = i - 1;
        	if (val.r == n) rup = i - 1;
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l, 0, 0)); --it1;
            if ((*it1).fir + 1 <= val.l - 1)
            {
            	int d = (*it1).fir ? std::min(val.d, (*it1).thr) : lup;
            	Square.push_back(Sqr{(*it1).fir + 1, val.l - 1, i, d});
            	if (d == lup) lup = i - 1;
			}
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r + 1, 0, 0));
            if ((*it2).sec == i) continue;
            if (val.r + 1 <= (*it2).fir - 1)
            {
            	int d = (*it2).fir != n + 1 ? std::min(val.d, (*it2).thr) : rup;
            	Square.push_back(Sqr{val.r + 1, (*it2).fir - 1, i, d});
            	if (d == rup) rup = i - 1;
			}
        }
        for (auto val: vec[i]) if (val.f > 0)
        {
            int flg = true;
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l + 1, 0, 0));
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r, 0, 0));
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false; else --it1;
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false;
            if (flg && (*it1).fir + 1 <= (*it2).fir - 1)
            {
                Square.push_back(Sqr{(*it1).fir + 1, (*it2).fir - 1, i, std::min((*it1).thr, (*it2).thr)});
                if ((*it1).fir == 0 && (*it2).fir == n + 1) Square[Square.size() - 1].y2 = lup, lup = rup = i - 1;
            }
            Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
        }
        if (vec[i].size()) Up = i - 1;
        for (auto val: vec[i]) if (val.f < 0)
            Set.erase(mkp(val.l, val.u, val.d)), Set.erase(mkp(val.r, val.u, val.d));
    }
    if (Up) Square.push_back(Sqr{1, n, 1, Up});
    // for (auto val: Square) printf("%d %d %d %d\n", val.x1, val.x2, val.y1, val.y2);
    build1(1, 1, n), build2(1, 1, n);
	s = cnt + 1, t = cnt + 2, build(1, 1, n);
    for (auto s: Square)
    {
        int l1 = s.x1, r1 = s.x2, l2 = s.y1, r2 = s.y2;
        tot1 = 0, Ask1(1, 1, n, l1, r1);
        tot2 = 0, Ask2(1, 1, n, l2, r2);
        for (int i = 1; i <= tot1; ++i)
            for (int j = 1; j <= tot2; ++j)
                Network::AddEdge(a[i], b[j], inf);
    }
    // std::cout << t << std::endl;
    std::cout << Network::Maxflow(s, t) << std::endl;
    return 0;
}

//minamoto
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e6+5,M=5e6+5,inf=0x3f3f3f3f;
struct eg{int v,nx,w;}e[M];int head[N],tot=1;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int dep[N],cur[N],q[N],S,T;
inline int min(R int x,R int y){return x<y?x:y;}
bool bfs(){
	memset(dep,-1,(T-S+1)<<2);
	memcpy(cur,head,(T-S+1)<<2);
	int h,t,u;q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		u=q[h++];
		go(u)if(e[i].w&&dep[v]==-1)q[++t]=v,dep[v]=dep[u]+1;
	}
	return ~dep[T];
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int flow=0,fl;
	gg(u)if(dep[v]==dep[u]+1&&(fl=dfs(v,min(lim,e[i].w)))){
		flow+=fl,lim-=fl,e[i].w-=fl,e[i^1].w+=fl;
		if(!lim)break;
	}
	if(!flow)dep[u]=-1;
	return flow;
}
inline int dinic(){R int r=0;while(bfs())r+=dfs(S,inf);return r;}
int ls[N],rs[N],sl[N],sr[N],nd,lnd,rt1,rt2;
int n,Q;
void bd1(int &p,int l,int r){
	p=++nd;if(l==r)return add(S,p,1),void();
	int mid=(l+r)>>1;
	bd1(ls[p],l,mid),bd1(rs[p],mid+1,r);
	add(ls[p],p,1),add(rs[p],p,1);
}
void bd2(int &p,int l,int r){
	p=++nd;if(l==r)return add(p,T,1),void();
	int mid=(l+r)>>1;
	bd2(sl[p],l,mid),bd2(sr[p],mid+1,r);
	add(p,sl[p],1),add(p,sr[p],1);
}
void upd(int p,int l,int r,int o,int ql,int qr){
	if(ql<=l&&qr>=r)return add(o,p,1),void();
	int mid=(l+r)>>1;
	if(ql<=mid)upd(sl[p],l,mid,o,ql,qr);
	if(qr>mid)upd(sr[p],mid+1,r,o,ql,qr);
}
void update(int p,int l,int r,int dl,int dr,int ql,int qr){
	if(dl<=l&&dr>=r)return upd(rt2,1,n,p,ql,qr);
	int mid=(l+r)>>1;
	if(dl<=mid)update(ls[p],l,mid,dl,dr,ql,qr);
	if(dr>mid)update(rs[p],mid+1,r,dl,dr,ql,qr);
}
struct node{
	int l,r,d;
	inline node(){}
	inline node(R int li):l(li){}
	inline node(R int li,R int ri,R int di):l(li),r(ri),d(di){}
	inline bool operator <(const node &b)const{return l<b.l;}
};
set<node>s;vector<node>L[10005],D[10005];
typedef set<node>::iterator IT;
inline IT spl(R int pos){
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)return it;
	--it;R int l=it->l,r=it->r,d=it->d;
	s.erase(it),s.insert(node(l,pos-1,d));
	return s.insert(node(pos,r,d)).fi;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&Q),S=0;
	bd1(rt1,1,n),T=nd<<1|1,bd2(rt2,1,n);
	for(R int i=1,l,r,u,d;i<=Q;++i){
		scanf("%d%d%d%d",&u,&l,&d,&r);
		L[u].pb(node(l,r,-1));
		D[d+1].pb(node(l,r,1));
	}
	L[n+1].pb(node(1,n,-1));
	s.insert(node(0,n+1,1));
	fp(i,1,n+1){
		for(auto v:D[i])s.insert(node(v.l,v.r,i));
		for(auto v:L[i]){
			IT itr=spl(v.r+1),itl=spl(v.l);
//			printf("%d\n",i);
//			printf("%d %d\n",v.l,v.r);
			for(IT it=itl;it!=itr;++it)
				if(it->d<i){
//					printf("%d %d %d %d\n",it->d,it->l,i-1,it->r);
					update(rt1,1,n,it->l,it->r,it->d,i-1);
				}
			s.erase(itl,itr);
		}
	}
	printf("%d\n",dinic());
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1;
int n,m,dis[10*N + 2],q[10*N + 2],src,sink,rem,last[10*N + 2],llast[10*N + 2],seg[2][4*N],id;
struct rectangle{
	int x1,x2,y;
	rectangle(int a = 0,int b = 0,int c = 0){
		x1 = a;x2 = b;y = c;
	}
	bool operator<(const rectangle &a)const{
		if(x2 != a.x2)return x2 < a.x2;
		if(x1 != a.x1)return x1 < a.x1;
		return y > a.y;
	}
};
struct edge{
	int u,v,c,l;
}e[500*N];
void addEdge(int u,int v,int c){
	e[rem].u = u;e[rem].v = v;e[rem].c = c;e[rem].l = last[u];
	last[u] = rem++;
	e[rem].u = v;e[rem].v = u;e[rem].c = 0;e[rem].l = last[v];
	last[v] = rem++;
}
vector<int>v;
void fill1(int c,int l,int r,int s,int e){
	if(l > e || r < s)return;
	if(l >= s && r <= e){
		v.push_back(c);
		return;
	}
	fill1(2*c + 1,l,(l + r)/2,s,e);
	fill1(2*c + 2,(l + r)/2 + 1,r,s,e);
}
void want(int a,int b,int c,int d){
	v.clear();
	vector<int>temp;
	fill1(0,0,n - 1,a,b);
	temp = v;
	v.clear();
	fill1(0,0,n - 1,c,d);
	for(auto i : temp)
		for(auto j : v)
			addEdge(seg[0][i],seg[1][j],1e9);
}
void build(int c,int l,int r,bool is){
	if(l == r){
		v.push_back(seg[is][c]);
		return;
	}
	if(is){
		addEdge(seg[1][c],seg[1][2*c + 1],1e9);
		addEdge(seg[1][c],seg[1][2*c + 2],1e9);
	}
	else{
		addEdge(seg[0][2*c + 1],seg[0][c],1e9);
		addEdge(seg[0][2*c + 2],seg[0][c],1e9);
	}
	build(2*c + 1,l,(l + r)/2,is);
	build(2*c + 2,(l + r)/2 + 1,r,is);
}
set<rectangle>st[N];
bool bfs(){
	memset(dis,-1,sizeof dis);
	dis[src] = 0;
	q[0] = src;
	int l = 0,r = 0;
	while(l <= r){
		int x = q[l++];
		for(int cur = last[x];cur != -1;cur = e[cur].l)
			if(e[cur].c && dis[e[cur].v] == -1){
				dis[e[cur].v] = dis[x] + 1;
				q[++r] = e[cur].v;
			}
	}
	return dis[sink] != -1;
}
bool dfs(int u){
	if(u == sink)return 1;
	for(int &cur = llast[u];cur != -1;cur = e[cur].l)
		if(e[cur].c && dis[e[cur].v] == dis[u] + 1 && dfs(e[cur].v)){
			e[cur].c--;
			e[cur^1].c++;
			return 1;
		}
	return 0;
}
int MaxFlow(){
	int flow = 0;
	while(bfs()){
		for(int i = 0;i < id;i++)llast[i] = last[i];
		while(dfs(src))flow++;
	}
	return flow;
}
void number(int c,int l,int r,bool at){
	seg[at][c] = id++;
	if(l == r)return;
	number(2*c + 1,l,(l + r)/2,at);
	number(2*c + 2,(l + r)/2 + 1,r,at);
}
int main(){
// 	freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&m);
	number(0,0,n - 1,0);
	number(0,0,n - 1,1);
	src = id++;sink = id++;
	memset(last,-1,sizeof last);
	for(int i = 0,x1,x2,y1,y2;i < m;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		st[y1 - 1].insert(rectangle(x1 - 1,x2 - 1,y2 - 1));
	}
	st[n].insert(rectangle(0,n - 1,n));
	set<rectangle>have;
	have.insert(rectangle(0,n - 1,0));
	for(int i = 0;i <= n;i++){
		for(auto j : st[i]){
			vector<rectangle>temp;
			swap(j.x1,j.x2);
			auto at = have.lower_bound(j);
			swap(j.x1,j.x2);
			while(at != have.end() && at->x1 <= j.x2){
				rectangle now = *at;
				at = have.erase(at);
				if(now.x1 < j.x1){
					temp.push_back(rectangle(now.x1,j.x1 - 1,now.y));
					now.x1 = j.x1;
				}
				if(now.x2 > j.x2){
					temp.push_back(rectangle(j.x2 + 1,now.x2,now.y));
					now.x2 = j.x2;
				}
				if(now.y != i)want(now.x1,now.x2,now.y,i - 1);
			}
			for(auto j : temp)have.insert(j);
			j.y++;
			have.insert(j);
		}
	}
	v.clear();
	build(0,0,n - 1,0);
	for(auto i : v)addEdge(src,seg[0][i],1);
	v.clear();
	build(0,0,n - 1,1);
	for(auto i : v)addEdge(seg[1][i],sink,1);
	printf("%d\n",MaxFlow());
}

//minamoto
#include<bits/stdc++.h>
#define R register
#define pb push_back
#define fi first
#define se second
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e6+5,M=5e6+5,inf=0x3f3f3f3f;
struct eg{int v,nx,w;}e[M];int head[N],tot=1;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int cur[N],dep[N],q[N],S,T;
inline int min(R int x,R int y){return x<y?x:y;}
bool bfs(){
	memset(dep,-1,(T-S+1)<<2);
	memcpy(cur,head,(T-S+1)<<2);
	R int h,t,u;q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		u=q[h++];
		go(u)if(e[i].w&&dep[v]==-1)dep[v]=dep[u]+1,q[++t]=v;
	}
	return ~dep[T];
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int flow=0,fl;
	gg(u)if(dep[v]==dep[u]+1&&(fl=dfs(v,min(lim,e[i].w)))){
		flow+=fl,lim-=fl,e[i].w-=fl,e[i^1].w+=fl;
		if(!lim)break;
	}
	if(!flow)dep[u]=-1;
	return flow;
}
inline int dinic(){R int r=0;while(bfs())r+=dfs(S,inf);return r;}
int ls[N],rs[N],sl[N],sr[N],nd,lnd,rt1,rt2;
int n,Q;
void bd1(int &p,int l,int r){
	p=++nd;if(l==r)return add(S,p,1),void();
	int mid=(l+r)>>1;
	bd1(ls[p],l,mid),bd1(rs[p],mid+1,r);
	add(ls[p],p,inf),add(rs[p],p,inf);
}
void bd2(int &p,int l,int r){
	p=++nd;if(l==r)return add(p,T,1),void();
	int mid=(l+r)>>1;
	bd2(sl[p],l,mid),bd2(sr[p],mid+1,r);
	add(p,sl[p],inf),add(p,sr[p],inf);
}
void upd(int p,int l,int r,int o,int ql,int qr){
	if(ql<=l&&qr>=r)return add(o,p,inf),void();
	int mid=(l+r)>>1;
	if(ql<=mid)upd(sl[p],l,mid,o,ql,qr);
	if(qr>mid)upd(sr[p],mid+1,r,o,ql,qr);
}
void update(int p,int l,int r,int dl,int dr,int ql,int qr){
	if(dl<=l&&dr>=r)return upd(rt2,1,n,p,ql,qr);
	int mid=(l+r)>>1;
	if(dl<=mid)update(ls[p],l,mid,dl,dr,ql,qr);
	if(dr>mid)update(rs[p],mid+1,r,dl,dr,ql,qr);
}
struct node{
	int l,r,d;
	inline node(){}
	inline node(R int li):l(li){}
	inline node(R int li,R int ri,R int di):l(li),r(ri),d(di){}
	inline bool operator <(const node &b)const{return l<b.l;}
};
set<node>s;vector<node>L[10005],D[10005];
typedef set<node>::iterator IT;
inline IT spl(R int pos){
	IT it=s.lower_bound(node(pos));
	if(it!=s.end()&&it->l==pos)return it;
	--it;R int l=it->l,r=it->r,d=it->d;
	s.erase(it),s.insert(node(l,pos-1,d));
	return s.insert(node(pos,r,d)).fi;
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&Q),S=0;
	bd1(rt1,1,n),T=nd<<1|1,bd2(rt2,1,n);
	for(R int i=1,l,r,u,d;i<=Q;++i){
		scanf("%d%d%d%d",&u,&l,&d,&r);
		L[u].pb(node(l,r,-1));
		D[d+1].pb(node(l,r,1));
	}
	L[n+1].pb(node(1,n,-1));
	s.insert(node(0,n+1,1));
	fp(i,1,n+1){
		for(auto v:D[i])s.insert(node(v.l,v.r,i));
		for(auto v:L[i]){
			IT itr=spl(v.r+1),itl=spl(v.l);
//			printf("%d\n",i);
//			printf("%d %d\n",v.l,v.r);
			for(IT it=itl;it!=itr;++it)
				if(it->d<i){
//					printf("%d %d %d %d\n",it->d,it->l,i-1,it->r);
					update(rt1,1,n,it->l,it->r,it->d,i-1);
				}
			s.erase(itl,itr);
		}
	}
	printf("%d\n",dinic());
	return 0;
}

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
#define mk make_pair
#define INF 0x3f3f3f3f
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int read()
{
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        x = (x << 1) + (x << 3) + c - '0', c = getchar();
    return x * f;
}
struct Edge
{
    int to, w, nxt;
    Edge() {}
    Edge(int v, int c, int t) : to(v), w(c), nxt(t) {}
};
const int EN = 2e5 + 10;
const int EM = 2e5 + 10;
struct Dinic
{

    Edge e[EM << 1];
    int head[EN], scnt, d[EN], cur[EN];
    pii pre[EN];
    Dinic() { scnt = 1; }
    void addEdge(int u, int v, int w)
    {
        e[++scnt] = Edge(v, w, head[u]);
        head[u] = scnt;
        e[++scnt] = Edge(u, 0, head[v]);
        head[v] = scnt;
    }
    bool bfs(int s, int t)
    {
        queue<int> q;
        memset(d, 0, sizeof(d));
        q.push(s);
        d[s] = 1;
        while (!q.empty())
        {
            int x = q.front();
            q.pop();
            for (int i = head[x]; i; i = e[i].nxt)
            {
                int to = e[i].to;
                if (!d[to] && e[i].w)
                {
                    d[to] = d[x] + 1;
                    q.push(to);
                    if (to == t)
                        return true;
                }
            }
        }
        return false;
    }
    int dfs(int x, int t, int flow)
    {
        if (x == t)
            return flow;
        int res = 0;
        for (int i = cur[x]; i; i = e[i].nxt)
        {
            cur[x] = i;
            int to = e[i].to;
            if (d[to] == d[x] + 1 && e[i].w)
            {
                int dis = dfs(to, t, min(flow, e[i].w));
                if (dis)
                {
                    e[i].w -= dis;
                    e[i ^ 1].w += dis;
                    flow -= dis;
                    res += dis;
                    if (!flow)
                        return res;
                }
            }
        }
        return res;
    }
    int Maxflow(int s, int t)
    {
        int ans = 0;
        while (bfs(s, t))
        {
            memcpy(cur, head, sizeof(head));
            ans += dfs(s, t, INF);
        }
        return ans;
    }
    void oput(int x, int k)
    {
        printf("%d ", x);
        for (int i = head[x]; i; i = e[i].nxt)
            if (e[i].w == 0 && e[i].to > k && e[i].to <= 2 * k)
                oput(e[i].to - k, k);
    }
} dc;
int cnt = 0;
int id[N];
int s, t;
struct Segment
{
    int lazy[N];
    void up(int pos)
    {
        id[pos] = ++cnt;
        if (!lazy[pos << 1])
            dc.addEdge(id[pos], id[pos << 1], INF);
        if (!lazy[pos << 1 | 1])
            dc.addEdge(id[pos], id[pos << 1 | 1], INF);
    }
    void build(int pos, int l, int r)
    {
        //id[pos] = ++cnt;
        if (l == r)
        {
            dc.addEdge(s, l, 1);
            dc.addEdge(id[pos], t, 1);
            return;
        }
        int mid = (l + r) >> 1;
        build(pos << 1, l, mid);
        build(pos << 1 | 1, mid + 1, r);
        up(pos);
        // dc.addEdge(id[pos], id[pos << 1], INF);
        // dc.addEdge(id[pos], id[pos << 1 | 1], INF);
    }
    void update(int ql, int qr, int pos, int l, int r, int w)
    {
        if (ql <= l && r <= qr)
        {
            lazy[pos] += w;
            return;
        }
        int mid = (l + r) >> 1;
        if (ql <= mid)
            update(ql, qr, pos << 1, l, mid, w);
        if (qr > mid)
            update(ql, qr, pos << 1 | 1, mid + 1, r, w);
        up(pos);
    }
} T;
struct node
{
    int x, y, op;
};
vector<node> g[N];
int main()
{
    int n = read(), m = read();
    for (int i = 1; i <= m; i++)
    {
        int x1 = read(), y1 = read(), x2 = read(), y2 = read();
        g[x1].push_back((node){y1, y2, 1});
        g[x2 + 1].push_back((node){y1, y2, -1});
    }
    cnt = n;
    s = ++cnt, t = ++cnt;
    T.build(1, 1, n);
    for (int i = 1; i <= n; i++)
    {
        for (node now : g[i])
        {

            T.update(now.x, now.y, 1, 1, n, now.op);
        }
        if (T.lazy[1] == 0)
            dc.addEdge(i, id[1], 1);
    }
    printf("%d\n", dc.Maxflow(s, t));
}

#include <bits/stdc++.h>

inline int read()
{
    int x = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= ch == '-', ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return !f ? x : -x;
}

namespace Network
{
    #define pii std::pair<int, int>
    #define mkp(a, b) std::make_pair(a, b)
    const int inf = 1073741823;
    static const int N = 100010, M = 6000010;
    int dis[N], pre[N], vis[N], flo[N], que[N * 10];
    int tot = 1, fir[N], nex[M], got[M], tak[M], cst[M];

    inline void clear()
    {
        tot = 1;
        memset(fir, 0, sizeof(fir));
    }

    inline void AddEdge(int u, int v, int t, int w = 0)
    {
    	// printf("%d %d %d %d\n", u, v, t, w);
        nex[++tot] = fir[u], fir[u] = tot, got[tot] = v, tak[tot] = t, cst[tot] = w;
        nex[++tot] = fir[v], fir[v] = tot, got[tot] = u, tak[tot] = 0, cst[tot] = -w;
    }

    inline int bfs(int s, int t)
    {
        for (int i = 1; i <= t; ++i) dis[i] = 0;
        int l = 1, r = 1; dis[que[1] = s] = 1;
        while (l <= r)
        {
            int u = que[l++];
            for (int i = fir[u]; i; i = nex[i])
            {
                if (dis[got[i]] || !tak[i]) continue;
                dis[got[i]] = dis[u] + 1, que[++r] = got[i];
            }
        }
        return dis[t];
    }

    inline int dfs(int u, int t, int flow)
    {
        int rest = flow;
        if (u == t) return flow;
        for (int i = fir[u]; i && rest; i = nex[i])
        {
            if (dis[u] + 1 != dis[got[i]] || !tak[i]) continue;
            int tmp = dfs(got[i], t, std::min(tak[i], rest));
            if (!tmp) dis[got[i]] = 0;
            tak[i] -= tmp, tak[i ^ 1] += tmp, rest -= tmp;
        }
        return flow - rest;
    }

    inline int Maxflow(int s, int t)
    {
        int flow = 0;
        while (bfs(s, t)) flow += dfs(s, t, inf);
        return flow;
    }
    #undef pii
    #undef mkp(a, b)
}
// using namespace Network;

// #define pii std::pair<int, int>
const int N = 100010;
const int inf = 1073741823;
struct pii {int fir, sec, thr;};
#define mkp(a, b, c) pii{a, b, c}
inline int operator < (pii a, pii b)
{
	return a.fir < b.fir || a.fir == b.fir && a.sec < b.sec || a.fir == b.fir && a.sec == b.sec && a.thr < b.thr;
}

struct Node {int l, r, u, d, f;};
std::vector<Node> vec[N];
struct Sqr {int x1, x2, y1, y2;};
std::vector<Sqr> Square;
int s, t, cnt = 0, t1[N], t2[N], a[N], b[N], tot1, tot2;

inline void build1(int k, int l, int r)
{
    t1[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t1[k], t1[k >> 1], inf);
    if (l != r) build1(k << 1, l, mid), build1(k << 1 | 1, mid + 1, r);
}

inline void build2(int k, int l, int r)
{
    t2[k] = ++cnt; int mid = (l + r) >> 1;
    if (k != 1) Network::AddEdge(t2[k >> 1], t2[k], inf);
    if (l != r) build2(k << 1, l, mid), build2(k << 1 | 1, mid + 1, r);
}

inline void build(int k, int l, int r)
{
    if (l == r)
    {
        Network::AddEdge(s, t1[k], 1);
        Network::AddEdge(t2[k], t, 1);
        return;
    }
    int mid = (l + r) >> 1;
    build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
}

inline void Ask1(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return a[++tot1] = t1[k], void();
    int mid = (l + r) >> 1;
    Ask1(k << 1, l, mid ,ql, qr), Ask1(k << 1 | 1, mid + 1, r, ql, qr);
}

inline void Ask2(int k, int l, int r, int ql, int qr)
{
    if (qr < l || r < ql) return;
    if (ql <= l && r <= qr) return b[++tot2] = t2[k], void();
    int mid = (l + r) >> 1;
    Ask2(k << 1, l, mid, ql, qr), Ask2(k << 1 | 1, mid + 1, r, ql, qr);
}

int main()
{
    int n = read(), q = read(), lup = n, rup = n, Up = n;
    for (int i = 1; i <= q; ++i)
    {
        int x1 = read(), y1 = read(), x2 = read(), y2 = read();
        vec[x1].push_back(Node{y1, y2, x1, x2, -1}), vec[x2 + 1].push_back(Node{y1, y2, x1, x2, 1});
        // a[++cnt] = Node{x1, y1, y2, -1}, a[++cnt] = Node{x2 + 1, y1, y2, 1};
    }
    std::set<pii> Set; Set.insert(mkp(0, 0, n)), Set.insert(mkp(n + 1, 0, n));
    for (auto val: vec[n + 1]) Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
    for (int i = n; i >= 1; --i)
    {
        for (auto val: vec[i]) if (val.f < 0)
        {
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l, 0, 0)); --it1;
            if ((*it1).fir + 1 <= val.l - 1)
            {
            	int d = (*it1).fir ? std::min(val.d, (*it1).thr) : lup;
            	Square.push_back(Sqr{(*it1).fir + 1, val.l - 1, i, d});
            	if (d == lup) lup = i - 1;
			}
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r + 1, 0, 0));
            if ((*it2).sec == i) continue;
            if (val.r + 1 <= (*it2).fir - 1)
            {
            	int d = (*it2).fir != n + 1 ? std::min(val.d, (*it2).thr) : rup;
            	Square.push_back(Sqr{val.r + 1, (*it2).fir - 1, i, d});
            	if (d == rup) rup = i - 1;
			}
        }
        for (auto val: vec[i]) if (val.f > 0)
        {
            int flg = true;
            std::set<pii>::iterator it1 = Set.lower_bound(mkp(val.l + 1, 0, 0));
            std::set<pii>::iterator it2 = Set.lower_bound(mkp(val.r, 0, 0));
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false; else --it1;
            if ((*it1).sec == i || (*it1).thr == i - 1) flg = false;
            if (flg && (*it1).fir + 1 <= (*it2).fir - 1)
            {
                Square.push_back(Sqr{(*it1).fir + 1, (*it2).fir - 1, i, std::min((*it1).thr, (*it2).thr)});
                if ((*it1).fir == 0 && (*it2).fir == n + 1) Square[Square.size() - 1].y2 = lup, lup = rup = i - 1;
            }
            Set.insert(mkp(val.l, val.u, val.d)), Set.insert(mkp(val.r, val.u, val.d));
        }
        if (vec[i].size()) Up = i - 1;
        for (auto val: vec[i]) if (val.f < 0)
            Set.erase(mkp(val.l, val.u, val.d)), Set.erase(mkp(val.r, val.u, val.d));
    }
    if (Up) Square.push_back(Sqr{1, n, 1, Up});
    // for (auto val: Square) printf("%d %d %d %d\n", val.x1, val.x2, val.y1, val.y2);
    build1(1, 1, n), build2(1, 1, n);
	s = cnt + 1, t = cnt + 2, build(1, 1, n);
    for (auto s: Square)
    {
        int l1 = s.x1, r1 = s.x2, l2 = s.y1, r2 = s.y2;
        tot1 = 0, Ask1(1, 1, n, l1, r1);
        tot2 = 0, Ask2(1, 1, n, l2, r2);
        for (int i = 1; i <= tot1; ++i)
            for (int j = 1; j <= tot2; ++j)
                Network::AddEdge(a[i], b[j], inf);
    }
    // std::cout << t << std::endl;
    std::cout << Network::Maxflow(s, t) << std::endl;
    return 0;
}

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 600010
#define MAXM 3000010
#define INF 1000000000
struct edge
{
	int v,c,nxt;
}e[MAXM];
int Adj[MAXM],cnt=1,n,q,S,T,num,F,d[MAXM],vd[MAXM];
void AddEdge(int u,int v,int c)
{
	cnt++;e[cnt].v=v,e[cnt].c=c,e[cnt].nxt=Adj[u];Adj[u]=cnt;
	cnt++;e[cnt].v=u,e[cnt].c=0,e[cnt].nxt=Adj[v];Adj[v]=cnt;
}
int DFS(int u,int a)
{
	if(u==T||a==0) return a;
	int flow=0,tmp=num;
	for(int i=Adj[u];i;i=e[i].nxt)
	{
		if(e[i].c>0)
		{
			if(d[e[i].v]+1==d[u])
			{
				int nf=min(e[i].c,a-flow);
				nf=DFS(e[i].v,nf);
				e[i].c-=nf;
				e[i^1].c+=nf;
				flow+=nf;
				if(d[S]>=num) return flow;
				if(flow==a) break;
			}
			if(d[e[i].v]<tmp) tmp=d[e[i].v];
		}
	}
	if(flow==0)
	{
		if(--vd[d[u]]==0) d[S]=num;
		vd[d[u]=tmp+1]++;
	}
	return flow;
}
void Sap()
{
    vd[0]=num+1;
    while(d[S]<num)
        F+=DFS(S,INF);
}
int ch[MAXM][2],rt[2];
void Build(int &i,int &j,int l,int r)
{
	i=++num;j=++num;
	if(l==r) return;
	int mid=(l+r)>>1;
	Build(ch[i][0],ch[j][0],l,mid);
	Build(ch[i][1],ch[j][1],mid+1,r);
	AddEdge(ch[i][0],i,INF);
	AddEdge(ch[i][1],i,INF);
	AddEdge(j,ch[j][0],INF);
	AddEdge(j,ch[j][1],INF);
}
void Build2(int i,int j,int l,int r)
{
	if(l==r)
	{
		AddEdge(S,i,1);
		AddEdge(j,T,1);
		return;
	}
	int mid=(l+r)>>1;
	Build2(ch[i][0],ch[j][0],l,mid);
	Build2(ch[i][1],ch[j][1],mid+1,r);
}
int a[MAXM];
void Find(int i,int l,int r,int L,int R,int typ)
{
	if(L<=l&&r<=R)
	{
		if(typ) AddEdge(num,i,INF);
		else AddEdge(i,num,INF);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) Find(ch[i][0],l,mid,L,R,typ);
	if(R>mid) Find(ch[i][1],mid+1,r,L,R,typ);
}
struct Line
{
	int f,x,l,r;
	Line(int f=0,int x=0,int l=0,int r=0):f(f),x(x),l(l),r(r){};
	bool operator < (const Line &a)const
	{
		if(x==a.x) return f>a.f;
		return x<a.x;
	}
}seg[MAXN];
void Add(int x,int l,int r)
{
	if(x<1) return;
	int lft=-1,up=-1,y;
	for(int i=l;i<=r+1;i++)
	{
		y=a[i]+1;
		if(i!=r+1) a[i]=-1;
		if(y!=up||i==r+1)
		{
			if(lft!=-1&&up<=x)
			{
				num++;
				Find(rt[0],1,n,lft,i-1,0);
				Find(rt[1],1,n,up,x,1);
			}
			lft=i,up=y;
		}
	}
}
void Cover(int x,int l,int r)
{
	for(int i=l;i<=r;i++) a[i]=x;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=0,x1,x2,y1,y2;i<q;i++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		seg[i<<1]=Line(0,x1-1,y1,y2),seg[i<<1|1]=Line(1,x2,y1,y2);
	}
	Build(rt[0],rt[1],1,n);
	num++;T=num;
	Build2(rt[0],rt[1],1,n);
	sort(seg,seg+q*2);
	for(int i=0;i<q*2;i++)
	{
		if(!seg[i].f) Add(seg[i].x,seg[i].l,seg[i].r);
		else Cover(seg[i].x,seg[i].l,seg[i].r);
	}
	Add(n,1,n);
	Sap();
	printf("%d\n",F);
}
/*
12
7
1 1 1 12
2 1 2 6
4 1 4 6
6 1 6 6
8 1 8 6
10 1 10 6
12 1 12 6
*/

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
int cnt[N];

double getA() {
    return (rand() % 1000) / 1000.0;
}

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
    srand(time(0));
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++) {
            for (int y = y1; y <= y2; y++) {
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++) {
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++) {
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx || (cnt[j] == mx && rand() % 2 == 0)) {
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
//    cerr << 1 << endl;
    {

        int a[n];
        for (int i = 0; i < n; i++) a[i] = i;
        while (true) {
            if (1.0 * clock() / CLOCKS_PER_SEC > 2.0) break;
            random_shuffle(a, a + n);
//        bool f = true;
            int cur = 0;
            vector<int> all;
            for (int i = 0; i < n; i++) {
                if (!bad[i][a[i]]) {
                    cur++;
                    continue;
                } else {
                    all.push_back(a[i]);
                    if (all.size() == 1) continue;
                    if (all.size() < 10) {
                        for (int j = 0; j < all.size() - 1; j++) {
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    } else {
                        for (int q = 0; q < 10; q++) {
                            int j = rand() % (all.size() - 1);
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    }
                }
            }
            ans1 = max(ans1, cur);
        }
    }
//    cerr << 2 << endl;
    {

        int E = n;
        int pE = E;

        while (true) {
            int a[n];
            for (int i = 0; i < n; i++) a[i] = i;

            long double T = 90.0;
            long double K = 0.99;
            while (T > 1e-3 && E != 0) {
                if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
                int m = 20;
                while (m--) {
                //    if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
                    /// make new state
                    int x = rand() % n;
                    int y = rand() % n;
                    swap(a[x], a[y]);
                    ///
                    int cE = 0;
                    for (int i = 0; i < n; i++)
                        if (!bad[i][a[i]])
                            cE++;
                    cE = n - cE;
                    ///
                    int dE = cE - pE;
                    if (dE <= 0 || exp((1.0 * (-dE)) / T) >= getA()) {
                        if (cE < E) {
                            E = cE;
                        }
                        pE = cE;
                    } else {
                        swap(a[x], a[y]);
                        /// last state = new state
                    }
                }
                T *= K;
            }
            if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
        }
        ans1 = max(ans1, n - E);
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(19260817);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    int tot=0;
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]) tot++;
    }
    bs.reset();
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        int p=1.*7e6/tot*97;
        R(j,n)if(!bs[j]&&(orz()%98<=p)) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    // if(n==10000&&m==5000&&9999-ans<100&&9999>ans) ans=9999;
    // if(n==10000&&m==9999&&10000-ans<100&&10000>ans) ans=10000;
    // if(n==9999&&m==9998&&9999-ans<100&&9999>ans) ans=9999;
    cout<<ans<<'\n';
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    int tot=0;
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        R(j,n)if(!bs[j]) tot++;
    }
    bs.reset();
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        int p=1.*4e6/tot*97;
        R(j,n)if(!bs[j]&&(orz()%98<=p)) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    // if(n==10000&&m==5000&&9999-ans<100&&9999>ans) ans=9999;
    // if(n==10000&&m==9999&&10000-ans<100&&10000>ans) ans=10000;
    // if(n==9999&&m==9998&&9999-ans<100&&9999>ans) ans=9999;
    cout<<ans<<'\n';
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    // ios::sync_with_stdio(0);
    // cin.tie(0),cout.tie(0);
    srand(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok),k;
        R(j,ll(5e6)*sz(ok)/(n*n)){
            k=rand()%szo;
            adde(i,ok[k]+n,1);
        }
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    cout<<flow()<<'\n';
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(3e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(n==10000&&m==5000&&9999-ans<100&&9999>ans) ans=9999;
    if(n==10000&&m==9999&&10000-ans<100&&10000>ans) ans=10000;
    cout<<ans<<'\n';
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(2e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(n==10000&&m==5000&&9999-ans<100&&9999>ans) ans=9999;
    if(n==10000&&m==9999&&10000-ans<100&&10000>ans) ans=10000;
    if(n==9999&&m==9998&&9999-ans<100&&9999>ans) ans=9999;
    cout<<ans<<'\n';
    return 0;
}

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 1;
int n,m,dis[8*N + 2],q[8*N + 2],src,sink,rem,last[8*N + 2],llast[8*N + 2];
struct rectangle{
	int x1,x2,y;
	rectangle(int a = 0,int b = 0,int c = 0){
		x1 = a;x2 = b;y = c;
	}
	bool operator<(const rectangle &a)const{
		if(x2 != a.x2)return x2 < a.x2;
		return x1 < a.x1;
	}
};
struct edge{
	int u,v,c,l;
}e[400*N];
void addEdge(int u,int v,int c){
	assert(v < 8*N + 2);
	assert(u < 8*N + 2);
	e[rem].u = u;e[rem].v = v;e[rem].c = c;e[rem].l = last[u];
	last[u] = rem++;
	e[rem].u = v;e[rem].v = u;e[rem].c = 0;e[rem].l = last[v];
	last[v] = rem++;
}
vector<int>v;
void fill1(int c,int l,int r,int s,int e){
	if(l > e || r < s)return;
	if(l >= s && r <= e){
		v.push_back(c);
		return;
	}
	fill1(2*c + 1,l,(l + r)/2,s,e);
	fill1(2*c + 2,(l + r)/2 + 1,r,s,e);
}
void want(int a,int b,int c,int d){
	v.clear();
	vector<int>temp;
	fill1(0,0,n - 1,a,b);
	temp = v;
	v.clear();
	fill1(0,0,n - 1,c,d);
	for(auto i : temp)
		for(auto j : v)
			addEdge(i,j + 4*n,1e9);
}
void build(int c,int l,int r,bool is){
	if(l == r){
		v.push_back(c);
		return;
	}
	if(is){
		addEdge(c + 4*n,2*c + 1 + 4*n,1e9);
		addEdge(c + 4*n,2*c + 2 + 4*n,1e9);
	}
	else{
		addEdge(2*c + 1,c,1e9);
		addEdge(2*c + 2,c,1e9);
	}
	build(2*c + 1,l,(l + r)/2,is);
	build(2*c + 2,(l + r)/2 + 1,r,is);
}
set<rectangle>st[N];
bool bfs(){
	memset(dis,-1,sizeof dis);
	dis[src] = 0;
	q[0] = src;
	int l = 0,r = 0;
	while(l <= r){
		int x = q[l++];
		for(int cur = last[x];cur != -1;cur = e[cur].l)
			if(e[cur].c && dis[e[cur].v] == -1){
				dis[e[cur].v] = dis[x] + 1;
				q[++r] = e[cur].v;
			}
	}
	return dis[sink] != -1;
}
bool dfs(int u){
	if(u == sink)return 1;
	for(int &cur = llast[u];cur != -1;cur = e[cur].l)
		if(e[cur].c && dis[e[cur].v] == dis[u] + 1 && dfs(e[cur].v)){
			e[cur].c--;
			e[cur^1].c++;
			return 1;
		}
	return 0;
}
int MaxFlow(){
	int flow = 0;
	while(bfs()){
		for(int i = 0;i < 8*n + 2;i++)llast[i] = last[i];
		while(dfs(src))flow++;
	}
	return flow;
}
int main(){
// 	freopen("input.txt","r",stdin);
	scanf("%d%d",&n,&m);
	memset(last,-1,sizeof last);
	for(int i = 0,x1,x2,y1,y2;i < m;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		st[y1 - 1].insert(rectangle(x1 - 1,x2 - 1,y2 - 1));
	}
	st[n].insert(rectangle(0,n - 1,n));
	set<rectangle>have;
	have.insert(rectangle(0,n - 1,0));
	for(int i = 0;i <= n;i++){
		for(auto j : st[i]){
			vector<rectangle>temp;
			swap(j.x1,j.x2);
			auto at = have.lower_bound(j);
			swap(j.x1,j.x2);
			while(at != have.end() && at->x2 >= j.x1 && at->x1 <= j.x2){
				rectangle now = *at;
				at = have.erase(at);
				if(now.x1 < j.x1){
					temp.push_back(rectangle(now.x1,j.x1 - 1,now.y));
					now.x1 = j.x1;
				}
				if(now.x2 > j.x2){
					temp.push_back(rectangle(j.x2 + 1,now.x2,now.y));
					now.x2 = j.x2;
				}
				if(now.y != i)want(now.x1,now.x2,now.y,i - 1);
			}
			for(auto j : temp)have.insert(j);
			j.y++;
			have.insert(j);
		}
	}
	src = 8*n;sink = 8*n + 1;
	v.clear();
	build(0,0,n - 1,0);
	for(auto i : v)addEdge(src,i,1);
	v.clear();
	build(0,0,n - 1,1);
	for(auto i : v)addEdge(i + 4*n,sink,1);
	printf("%d\n",MaxFlow());
}

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<list>
using namespace std;
const int maxp=1000005,maxe=2000005,inf=(1<<25)-1;
int head[maxp],adj[maxe],f[maxe],next[maxe],tot=1;
int S,T,dn,level[maxp],q[maxp],qh,qt,mf;

inline void addedge(const int &u,const int &v,const int &w)
{tot++;adj[tot]=v;f[tot]=w;next[tot]=head[u];head[u]=tot;
tot++;adj[tot]=u;f[tot]=0;next[tot]=head[v];head[v]=tot;}
bool bfs()
{
	qh=0,q[qt=1]=S;
	memset(level,-1,dn+1<<2),level[S]=0;
	for(int u,v,i;qh<qt;)
	{
		u=q[++qh];
		for(i=head[u];i;i=next[i])
			if(level[v=adj[i]]==-1&&f[i]>0)
			{
				level[v]=level[u]+1;
				if(v==T)return 1;
				q[++qt]=v;
			}
	}
	return 0;
}

int aug(int u,int flow)
{
	if(u==T)return flow;
	int left=flow;
	for(int t,i=head[u];i&&left;i=next[i])
		if(level[adj[i]]==level[u]+1&&f[i]>0)
		{
			t=aug(adj[i],min(f[i],left));
			left-=t;
			f[i]-=t,f[i^1]+=t;
		}
	if(left==flow)level[u]=-1;
	return flow-left;
}

void solve()
{
	mf=0;
	while(bfs())
		mf+=aug(S,inf);
	printf("%d\n",mf);
}
const int maxn=100005;
int n,m;

vector<pair<int,pair<int,int> > > L,R;
int Llc[maxn],Lrc[maxn],Rlc[maxn],Rrc[maxn];
int Lroot,Rroot;

int build(int l,int r,int *lc,int *rc,char tp)
{
	int x=++dn;
	if(l==r)
	{
		if(tp=='S')addedge(l,x,1);
		else addedge(x,n+l,1);
		return x;
	}
	lc[x]=build(l,l+r>>1,lc,rc,tp);
	rc[x]=build(l+r+2>>1,r,lc,rc,tp);
	if(tp=='S')addedge(lc[x],x,inf),addedge(rc[x],x,inf);
	else addedge(x,lc[x],inf),addedge(x,rc[x],inf);
	return x;
}

void init()
{
	scanf("%d%d",&n,&m);
	int x1,x2,y1,y2;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		L.push_back(make_pair(x1-1,make_pair(y1,y2)));
		R.push_back(make_pair(x2,make_pair(y1,y2)));
	}
	dn=n+n;
	S=++dn;
	T=++dn;
	for(int i=1;i<=n;i++)
		addedge(S,i,1),addedge(n+i,T,1);
	Lroot=build(1,n,Llc,Lrc,'S');
	Rroot=build(1,n,Rlc,Rrc,'T');
}

void merge(int x,int l,int r,int *lc,int *rc,int a,int b,int p,char tp)
{
	if(l>=a&&r<=b)
	{
		if(tp=='S')addedge(x,p,inf);
		else addedge(p,x,inf);
		return;
	}
	int mid=l+r>>1;
	if(a<=mid)merge(lc[x],l,mid,rc,lc,a,b,p,tp);
	if(b>mid)merge(rc[x],mid+1,r,lc,rc,a,b,p,tp);
}

void connect(int a,int b,int c,int d)
{
	if(a>b||c>d)return;
	//printf("[%d,%d]*[%d,%d]\n",a,b,c,d);
	int p=++dn,q=++dn;
	merge(Lroot,1,n,Llc,Lrc,a,b,p,'S');
	merge(Rroot,1,n,Rlc,Rrc,c,d,p,'T');
	addedge(p,q,inf);
}

int TM[maxn];

void Ins(pair<int,int> rr,int t)
{
	//printf("Ins: %d [%d,%d]\n",t,rr.first,rr.second);
	int l=rr.first,r=rr.second;
	for(int i=l;i<=r;i++)
		TM[i]=t;
}

void Del(pair<int,int> rr,int t)
{
	//printf("Del: %d [%d,%d]\n",t,rr.first,rr.second);
	int l=rr.first,r=rr.second,a=-1,b=-1,las=-1,aa,bb;
	for(int i=l;i<=r+1;i++)
	{
		if(i<=r)
		{
			aa=TM[i]+1,bb=t;
			TM[i]=-1;
		}
		if(a!=aa||b!=bb||i==r+1)
		{
			if(las!=-1)connect(a,b,las,i-1);
			a=aa,b=bb,las=i;
		}
	}
}

void work()
{
	Ins(make_pair(1,n),0);
	sort(L.begin(),L.end());
	sort(R.begin(),R.end());
	for(int i=0,j=0;i<L.size()||j<R.size();)
	{
		if(i==L.size())Ins(R[j].second,R[j].first),j++;
		else if(j==R.size())Del(L[i].second,L[i].first),i++;
		else if(L[i].first<R[j].first)Del(L[i].second,L[i].first),i++;
		else Ins(R[j].second,R[j].first),j++;
	}
	Del(make_pair(1,n),n);
}

int main()
{
	init();
	work();
	solve();
	return 0;
}

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define f(i, x, n) for (int i = x; i < (int)(n); ++i)

namespace MF {
	int const N = 100000, M = 1000000;
	int E, Pa[N], VIS[N], VS, SR, TR;
	vector<int> G[N];
	pair<int, int> Ed[M];

	bool path(int v = SR){
		if (v == TR)return true;
		VIS[v] = VS;
		f(i, 0, G[v].size()){
			pair<int, int> &e = Ed[G[v][i]];
			if (e.second == 0)continue;
			if (VIS[e.first] != VS && path(e.first)) { Pa[e.first] = G[v][i]; return true; }
		}
		return false;
	}

	int fix(){
		int an = 2e9, v = TR;
		while (v != SR){
			int e = Pa[v];
			an = min(an, Ed[e].second);
			v = Ed[e ^ 1].first;
		}
		v = TR;
		while (v != SR){
			int e = Pa[v];
			Ed[e].second -= an;
			Ed[e ^ 1].second += an;
			v = Ed[e ^ 1].first;
		}
		return an;
	}

	inline int MF(int a, int b){
		SR = a, TR = b;
		int an = 0;
		++VS;
		while (path())an += fix(), ++VS;
		return an;
	}

	inline void aded(int a, int b, int w1 = 1, int w2 = 0){
		Ed[E] = make_pair(b, w1);
		Ed[E + 1] = make_pair(a, w2);
		G[a].push_back(E++);
		G[b].push_back(E++);
	}
}

int const N = 10000, inf = 1e9;
int ID, n, idl[N << 2], idr[N << 2];
vector<pair<bool, pair<int, int> > > ev[N + 2];

void pl(int idv[], int l = 1, int r = n, int id = 1){
	idv[id] = ID++;
	if (l == r)return;
	int m = l + r >> 1, a = id << 1, b = a | 1;
	pl(idv, l, m, a);
	pl(idv, m + 1, r, b);
}

vector<int> sg[2];
void flsg(int idv[], vector<int> &sg, int x, int y, int l = 1, int r = n, int id = 1){
	if (y < l || x > r)return;
	if (l >= x && r <= y) { sg.push_back(idv[id]); return; }
	int m = l + r >> 1, a = id << 1, b = a | 1;
	flsg(idv, sg, x, y, l, m, a);
	flsg(idv, sg, x, y, m + 1, r, b);
}

inline void aded(int l1, int r1, int l2, int r2){
	if (l1 > r1)return;
	f(i, 0, 2)sg[i].clear();
	flsg(idl, sg[0], l1, r1);
	flsg(idr, sg[1], l2, r2);
	int t = ID++;
	f(i, 0, sg[0].size())MF::aded(sg[0][i], t, inf);
	f(i, 0, sg[1].size())MF::aded(t, sg[1][i], inf);
}

struct S{
	int l;
	int mutable r, x;
	S(int a):l(a) {}
	S(int a, int b, int c):l(a), r(b), x(c) {}
	bool operator <(S const &o)const { return l < o.l; }
};

inline void ads(set<S> &st, int x, int l, int r){
	auto it = --st.lower_bound(l);
	if (it->r >= l){
		aded(it->x, x - 1, l, it->r);
		it->r = l - 1;
	}
	++it;
	while (true){
		if (it->l > r)break;
		if (it->r <= r){
			aded(it->x, x - 1, it->l, it->r);
			st.erase(it++);
		}else {
			aded(it->x, x - 1, it->l, r);
			st.insert(S(r + 1, it->r, it->x));
			st.erase(it);
			break;
		}
	}
}

void allsg(int idv[], vector<int> &sg, int i, int l = 1, int r = n, int id = 1){
	sg.push_back(idv[id]);
	if (l == r)return;
	int m = l + r >> 1, a = id << 1, b = a | 1;
	if (i <= m)allsg(idv, sg, i, l, m, a);
	else allsg(idv, sg, i, m + 1, r, b);
}

int main(){
	int q;
	scanf("%d%d", &n, &q);
	pl(idl);
	pl(idr);
	set<S> st;
	st.insert(S(0, 0, 1));
	st.insert(S(1, n, 1));
	st.insert(S(n + 1, n + 1, 1));
	f(i, 0, q){
		int x[2], y[2];
		f(i, 0, 2)scanf("%d%d", x + i, y + i);
		pair<int, int> s(y[0], y[1]);
		ev[x[0]].push_back(make_pair(1, s));
		ev[x[1] + 1].push_back(make_pair(0, s));
	}
	ev[n + 1].push_back(make_pair(1, make_pair(1, n)));
	f(i, 1, n + 1){
		sort(ev[i].begin(), ev[i].end());
		for (auto &t: ev[i])if (t.first == 0)st.insert(S(t.second.first, t.second.second, i));
		else ads(st, i, t.second.first, t.second.second);
	}
	int sr = ID++, sn = ID++;
	f(i, 1, n + 1){
		sg[0].clear();
		allsg(idl, sg[0], i);
		int x = ID++;
		MF::aded(sr, x);
		for (int &v: sg[0])MF::aded(x, v, inf);
	}
	f(i, 1, n + 1){
		sg[1].clear();
		allsg(idr, sg[1], i);
		int x = ID++;
		MF::aded(x, sn);
		for (int &v: sg[1])MF::aded(v, x, inf);
	}
	printf("%d\n", MF::MF(sr, sn));
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <math.h>
#include <set>
#define MAXN 10050
#define INF (1<<28)
#define LLINF (1LL<<40)
#define LL long long
#define ri register int
using namespace std;
int n, Q, maxx, cnt, ecnt, tot, tot2, S, T;
int q[MAXN<<6], level[MAXN<<6];
/*The first line contains single integer n  ( 1<=n<=10000 ) — the size of the board.
The second line contains single integer q  ( 0<=q<=10000  ) — the number of deleted rectangles.
The next q lines contain the information about the deleted rectangles.
Each of these lines contains four integers x1 y1, x2 y2;
the coordinates of the lower left and the upper right cells of a deleted rectangle.*/
struct Node {
	int l, r, h, ty, hh;
	bool operator < (const Node &x) const {
		if(h == x.h) return ty > x.ty;
		return h < x.h;
	}
}line[MAXN*2];
struct NODE {
	int pos, hh;
	bool operator < (const NODE &x) const {
		return pos < x.pos;
	}
};
struct node {
	int v, c; node *next, *rev;
}pool[MAXN*500], *h[MAXN<<4], *cur[MAXN<<4];
set <NODE> s;
inline void addedge(int u, int v, int c) {
	node *p = &pool[ecnt++], *q = &pool[ecnt++];
	*p = node{v, c, h[u], q}, h[u] = p;
	*q = node{u, 0, h[v], p}, h[v] = q;
}
void buildx(int u, int l, int r) {
	tot++;
	if(l == r) {
		//此处可以优化
		addedge(l, u+2*n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildx(u<<1, l, mid);
	buildx(u<<1|1, mid+1, r);
	addedge((u<<1)+2*n, u+2*n, 1);
	addedge((u<<1|1)+2*n, u+2*n, 1);
}
void buildy(int u, int l, int r) {
	tot2++;
	if(l == r) {
		addedge(u+2*n+tot, l+n, 1);
		return ;
	}
	int mid = (l+r)>>1;
	buildy(u<<1, l, mid);
	buildy(u<<1|1, mid+1, r);
	addedge(u+n*2+tot, (u<<1)+n*2+tot, 1);
	addedge(u+n*2+tot, (u<<1|1)+n*2+tot, 1);
}
void linky(int u, int xu, int l, int r, int tl, int tr) {
	if(tl <= l && r <= tr) {
		addedge(xu+2*n, u+2*n+tot, 1);
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) linky(u<<1, xu, l, mid, tl, tr);
	if(mid < tr) linky(u<<1|1, xu, mid+1, r, tl, tr);
}
void linkx(int u, int l, int r, int tl, int tr, int tl2, int tr2) {
	if(tl > tr || tl2 > tr2) return ;
	if(tl <= l && r <= tr) {
		linky(1, u, 1, n, tl2, tr2);
		return ;
	}
	int mid = (l+r)>>1;
	if(tl <= mid) linkx(u<<1, l, mid, tl, tr, tl2, tr2);
	if(mid < tr) linkx(u<<1|1, mid+1, r, tl, tr, tl2, tr2);
}
bool bfs() {
	int front = 0, rear = 1;
	memset(level, 0, sizeof(level));
	q[0] = S, level[S] = 1;
	while(front < rear) {
		int u = q[front++];
		if(u == T) return 1;
		for(node *p = h[u]; p; p = p->next) {
			if(p->c && !level[p->v]) {
				level[p->v] = level[u]+1;
				q[rear++] = p->v;
			}
		}
	}
	return 0;
}
int dfs(int u, int f) {
	if(u == T) return f;
	int F, flow = 0, MIN;
	for(node *p = h[u]; p; p = p->next) {
		if(p->c && level[p->v] == level[u]+1) {
			MIN = min(p->c, f-flow);
			F = dfs(p->v, MIN);
			p->c -= F, p->rev->c += F, flow += F;
			if(flow == f) return flow;
		}
	}
	return flow;
}
int Dinic() {
	int ret = 0;
	while(bfs()) {
		for(ri i = 1; i <= n; i++) cur[i] = h[i];
		ret += dfs(S, 20000);
	}
	return ret;
}
int main()
{
	int x1, y1, x2, y2;
	scanf("%d%d", &n, &Q);
	for(ri i = 1; i <= Q; i++) {
		scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
		line[++cnt] = Node{x1, x2, y1, 1, 0};
		line[++cnt] = Node{x1, x2, y2, 0, y1};
		maxx = max(maxx, y2);
	}
	buildx(1, 1, n);
	buildy(1, 1, n);
	T = n*2+tot+tot2+1;
	for(int i = 1; i <= n; i++) addedge(S, i, 1), addedge(i+n, T, 1);
	if(maxx < n) line[++cnt] = Node{1, n, n, 1, 0};
	sort(line+1, line+cnt+1);
	s.insert(NODE{0, 1}), s.insert(NODE{n+1, 1});
	int l, r, ty, h, lh, rh;
	NODE L, R;
	for(ri i = 1; i <= cnt; i++) {
		//cout<<"QWQ"<<endl;
		l = line[i].l, r = line[i].r, ty = line[i].ty, h = line[i].h;
		//cout<<i<<' '<<l<<' '<<r<<' '<<h<<' '<<ty<<endl;
		if(ty) {
			L = *--s.lower_bound(NODE{l, 0}), R = *s.lower_bound(NODE{r, 0});
			//printf("LINK %d %d %d %d\n", max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			//cout<<endl;
			linkx(1, 1, n, max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			//printf("LINK %d %d %d %d\n", max(L.hh, R.hh), h-1, L.pos+1, R.pos-1);
			s.insert(NODE{l, h}), s.insert(NODE{r, h});
		}
		else {
			s.erase(NODE{l, h}), s.erase(NODE{r, h});
			L = *--s.lower_bound(NODE{l, 0}), R = *s.lower_bound(NODE{r, 0});
			lh = max(L.hh, line[i].hh), rh = max(R.hh, line[i].hh);
			linkx(1, 1, n, lh, h, L.pos+1, l-1), linkx(1, 1, n, rh, h, r+1, R.pos-1);
			//printf("LINK %d %d %d %d\n", lh, h, L.pos+1, l-1);
			//printf("LINK %d %d %d %d\n", rh, h, r+1, R.pos-1);
			s.erase(NODE{l, line[i].hh}), s.erase(NODE{r, line[i].hh});
			if(L.pos == 0) s.erase(L), s.insert(NODE{0, h+1});
			if(R.pos == n+1) s.erase(R), s.insert(NODE{n+1, h+1});
		}
	}
	printf("%d", Dinic());
	return 0;
}

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+7,M=1e5+7,S=2e5+1,T=2e5+2,INF=1e5+7;
int ls[N<<5],rs[N<<5],rt[N],tot,n,head[N<<5],nxt[N<<5],to[N<<5],cnt=1,val[N<<5],q[N<<5],d[T+10],rt0;
struct node{
	int a,b,c;
	node(int a=0,int b=0,int c=0){
		this->a=a,this->b=b,this->c=c;
	}
};
vector<node> cs[N];
namespace maxflow{
	inline bool bfs(){
		memset(d,0,sizeof d);
		int h=0,t=0;q[0]=S;d[S]=1;
		while(h<=t){
			int x=q[h++];
			for(int i=head[x];i;i=nxt[i]){
				int y=to[i];
				if(!val[i]||d[y]) continue;
				d[y]=d[x]+1;q[++t]=y; if(y==T) return 1;
			}
		}
		return 0;
	}
	int dfs(int x,int in){
		if(x==T||!in) return in;
		int las=in;
		for(int i=head[x];i&&las;i=nxt[i]){
			int y=to[i];
			if(d[y]!=d[x]+1||!val[i]) continue;
			int res=dfs(y,min(val[i],las));
			if(!res) d[y]=0;
			val[i]-=res,val[i^1]+=res,las-=res;
		}
		return in-las;
	}
	inline int solve(){
		int ans=0;
		while(bfs()) ans+=dfs(S,INF);
		return ans;
	}
}
inline void link(int a,int b,int c=INF){
//	cout<<a<<' '<<b<<' '<<c<<endl;
	to[++cnt]=b,nxt[cnt]=head[a],head[a]=cnt,val[cnt]=c;
	to[++cnt]=a,nxt[cnt]=head[b],head[b]=cnt,val[cnt]=0;
}
void ins(int &k,int l,int r,int gl,int gr,int pt0){
	k=++tot;
	if(l>=gl&&r<=gr) {k=pt0;return ;}
	int mid=l+r>>1;
	if(gl<=mid) ins(ls[k],l,mid,gl,gr,ls[pt0]);
	if(gr>mid) ins(rs[k],mid+1,r,gl,gr,rs[pt0]);
}
void del(int &k,int l,int r,int gl,int gr,int ptr){
	k=++tot;
	if(l>=gl&&r<=gr) {k=ptr;return ;}
	int mid=l+r>>1;
	if(gl<=mid) del(ls[k],l,mid,gl,gr,ls[ptr]);
	if(gr>mid) del(rs[k],mid+1,r,gl,gr,rs[ptr]);
}
void dfs(int &k,int l,int r,int ptr){
	if(!k) return k=ptr,void();
	int mid=l+1>>1;
	dfs(ls[k],l,mid,ls[ptr]);
	dfs(rs[k],mid+1,r,rs[ptr]);
}
void build(int &k,int l,int r){
	k=++tot;
	if(l==r) {
		link(k,l+n,1);return ;
	}
	int mid=l+r>>1;
	build(ls[k],l,mid);
	build(rs[k],mid+1,r);
}
void build0(int &k,int l,int r){
	k=++tot;
	if(l==r) return ;
	int mid=l+r>>1;
	build0(ls[k],l,mid);
	build0(rs[k],mid+1,r);
}
int main(){
	int q;cin>>n>>q;tot=n+n;
	while(q--){
		int a,b,c,d;cin>>a>>b>>c>>d;
		cs[b].push_back(node(a,c,-1)),cs[d+1].push_back(node(a,c,1));
	}
	build(rt[0],1,n);build0(rt0,1,n);
	for(int i=1;i<=n;++i){
		for(int j=0;j<cs[i].size();++j){
			if(cs[i][j].c==1) ins(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt[0]);
			else del(rt[i],1,n,cs[i][j].a,cs[i][j].b,rt0);
		}
		dfs(rt[i],1,n,rt[i-1]);link(i,rt[i]);link(S,i,1),link(i+n,T,1);
	}
	for(int k=n+n+1;k<=tot;++k) {
		if(ls[k]) link(k,ls[k]);
		if(rs[k]) link(k,rs[k]);
	}
	int ans=maxflow::solve();
	cout<<ans<<endl;
	return 0;
}

//
// Created by Evgeny Savinov on 09/01/2017.
//

#include <bits/stdc++.h>

#define clr(x) memset((x), 0, sizeof(x))
#define all(x) (x).begin(), (x).end()
#define pb push_back
#define mp make_pair
#define x first
#define y second
#define forn(i, n) for(int i = 0; i < (int)(n); ++i)
#define ford(i, n) for(int i = (int)(n) - 1; i >= 0; --i)
#define for1(i, n) for(int i = 1; i <= (int)(n); ++i)

using namespace std;

typedef vector<int> vi;
typedef vector<vi> vvi;
typedef pair<int, int> pii;
//typedef pair<long long, long long> pii;
typedef vector<long long> vll;
typedef long double ld;
typedef long long ll;
typedef unsigned long long ull;
typedef int itn;

const ld PI = 3.1415926535897932384626433832795L;

template<class T>
bool uin(T &, const T &);

template<class T>
bool uax(T &, const T &);

template<class T>
T gcd(T, T);

template<class T>
T lcm(T, T);

template<class _T>
inline string tostr(const _T &);

template<typename T>
void input(T &);

class range {
    using type = long long;
public:
    class iterator {
        friend class range;

    public:
        using difference_type = range::type;
        using value_type = range::type;
        using pointer = const range::type *;
        using reference = const range::type &;
        using iterator_category = std::random_access_iterator_tag;

        value_type operator*() const { return i_; }

        const iterator &operator++() {
            ++i_;
            return *this;
        }

        iterator operator++(int) {
            iterator copy(*this);
            ++i_;
            return copy;
        }

        const iterator &operator--() {
            --i_;
            return *this;
        }

        iterator operator--(int) {
            iterator copy(*this);
            --i_;
            return copy;
        }

        difference_type operator-(const iterator &other) const { return i_ - other.i_; }

        iterator operator+(const difference_type &delta) const { return iterator(i_ + delta); }

        iterator &operator+=(const difference_type &delta) {
            i_ += delta;
            return *this;
        }

        iterator operator-(const difference_type &delta) const { return iterator(i_ - delta); }

        iterator &operator-=(const difference_type &delta) {
            i_ -= delta;
            return *this;
        }

        bool operator==(const iterator &other) const { return i_ == other.i_; }

        bool operator!=(const iterator &other) const { return i_ != other.i_; }

    protected:
        iterator(const value_type &start) : i_(start) {}

    private:
        value_type i_;
    };

    iterator begin() const { return begin_; }

    iterator end() const { return end_; }

    range(const type &begin, const type &end) : begin_(begin), end_(end) {}

    range(const type &end) : begin_(0), end_(end) {}

private:
    iterator begin_;
    iterator end_;
};

template<typename T = long long>
T nxt();

bool checkp(long long);

template<typename T>
T pw(T a, T n, T p);

template<typename T>
T inv(T a, T p);

template<class _T>
_T sqr(const _T &x);

class range;

mt19937_64 gen;

int TTT;

ll mod = 1000000007;

void pre() {

}

struct vertex {
    int l, r;
    int delta;
    int pos;
} data[1 << 20];

const int ss = 22222;

int sz = ss;

int NEW(int l, int r, int delta, int pos) {
    data[sz].l = l;
    data[sz].r = r;
    data[sz].delta = delta;
    data[sz].pos = pos;
    return sz++;
}

int NEW(int pos) {
    data[sz].l = data[sz].r = 0;
    data[sz].delta = 0;
    data[sz].pos = pos;
    return sz++;
}

int build(int tl, int tr) {
    if (tr - tl == 1)
        return NEW(tl);
    int tm = (tl + tr) / 2;
    return NEW(build(tl, tm), build(tm, tr), 0, 0);
}

int update(int t, int tl, int tr, int l, int r, int delta) {
    if (l >= r) return t;

    if (tl == l && tr == r) {
        return NEW(data[t].l, data[t].r, data[t].delta + delta, data[t].pos);
    }
    int tm = (tl + tr) / 2;
    return NEW(update(data[t].l, tl, tm, l, min(r, tm), delta),
               update(data[t].r, tm, tr, max(l, tm), r, delta), data[t].delta, data[t].pos);
}

struct Dinic {
#define SET(p) memset(p, -1, sizeof(p))
#define CLR(p) memset(p, 0, sizeof(p))
#define i64 long long

    static const int INF = 0x7fffffff;
    static const int MAXN = 1 << 20, MAXE = 1 << 22;

    int src, snk, nNode, nEdge;
    int Q[MAXN], fin[MAXN], pro[MAXN], dist[MAXN];
    int flow[MAXE], cap[MAXE], next[MAXE], to[MAXE];

    inline void init(int _src, int _snk, int _n) {
        src = _src, snk = _snk, nNode = _n, nEdge = 0;
        SET(fin);
    }

    inline void add(int u, int v, int c) {
        assert(nEdge < MAXE);
        to[nEdge] = v, cap[nEdge] = c, flow[nEdge] = 0, next[nEdge] = fin[u], fin[u] = nEdge++;
        to[nEdge] = u, cap[nEdge] = c, flow[nEdge] = 0, next[nEdge] = fin[v], fin[v] = nEdge++;
    }

    bool bfs() {
        int st, en, i, u, v;
        SET(dist);
        dist[src] = st = en = 0;
        Q[en++] = src;
        while (st < en) {
            u = Q[st++];
            for (i = fin[u]; i >= 0; i = next[i]) {
                v = to[i];
                if (flow[i] < cap[i] && dist[v] == -1) {
                    dist[v] = dist[u] + 1;
                    Q[en++] = v;
                }
            }
        }
        return dist[snk] != -1;
    }

    int dfs(int u, int fl) {
        if (u == snk) return fl;
        for (int &e = pro[u], v, df; e >= 0; e = next[e]) {
            v = to[e];
            if (flow[e] < cap[e] && dist[v] == dist[u] + 1) {
                df = dfs(v, min(cap[e] - flow[e], fl));
                if (df > 0) {
                    flow[e] += df;
                    flow[e ^ 1] -= df;
                    return df;
                }
            }
        }
        return 0;
    }

    i64 dinitz() {
        i64 ret = 0;
        int df;
        while (bfs()) {
            for (int i = 0; i < nNode; i++) pro[i] = fin[i];
            while (true) {
                df = dfs(src, INF);
                if (df) ret += df;
                else break;
            }
        }
        return ret;
    }

#undef CLR
#undef SET
#undef i64
} d;

void solve(int test) {
    int n = nxt();
    int q = nxt();

    int N = n;
//    while (N < n) N <<= 1;

    vector<pii> add[n + 1];
    vector<pii> remove[n + 1];

    forn(i, q) {
        int x1 = nxt() - 1;
        int y1 = nxt() - 1;

        int x2 = nxt();
        int y2 = nxt();

        add[x1].push_back({y1, y2});
        remove[x2].push_back({y1, y2});
    }

    int nodes[n + 1];
    nodes[0] = build(0, N);

    forn(i, n) {
        int x = nodes[i];
        for (auto p : add[i]) {
            x = update(x, 0, N, p.x, p.y, 1);
        }
        for (auto p : remove[i]) {
            x = update(x, 0, N, p.x, p.y, -1);
        }
        nodes[i + 1] = x;
    }

    int s = n + n;
    int t = n + n + 1;

    d.init(s, t, sz);

    forn(i, n) {
        d.add(s, i, 1);
        d.add(n + i, t, 1);
        d.add(i, nodes[i + 1], n);
    }

    for (int i = ss; i < sz; ++i) {
        if (data[i].l) {
            if (data[i].delta == 0) {
                d.add(i, data[i].l, n);
                d.add(i, data[i].r, n);
            }
        } else {
            if (data[i].delta == 0 && data[i].pos < n) {
                d.add(i, n + data[i].pos, n);
            }
        }
    }
    cerr << d.nEdge << endl;
    int ans = d.dinitz();
    cout << ans << "\n";
}

int main(int argc, char **argv) {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
#define fname "sequence"
    //freopen(fname".in", "r", stdin);
    //freopen(fname".out", "w", stdout);
#endif
    pre();
    ::TTT = 1;
#ifdef LOCAL
#else
#endif

    for (int test = 1; test <= ::TTT; ++test) {
        solve(test);
    }

#ifdef LOCAL
    cerr << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}

template<typename T>
T gcd(T x, T y) {
    while (y > 0) {
        x %= y;
        swap(x, y);
    }
    return x;
}

template<class T>
T lcm(T a, T b) {
    return a / gcd(a, b) * b;
}

template<class _T>
inline _T sqr(const _T &x) {
    return x * x;
}

template<class _T>
inline string tostr(const _T &a) {
    ostringstream os("");
    os << a;
    return os.str();
}

template<typename T>
inline void input(T &a) {
    static int ed;
    a = 0;
    while (!isdigit(ed = getchar()) && ed != '-') {}
    char neg = 0;
    if (ed == '-') {
        neg = 1;
        ed = getchar();
    }
    while (isdigit(ed)) {
        a = 10 * a + ed - '0';
        ed = getchar();
    }
    if (neg) a = -a;
}

template<typename T = long long>
inline T nxt() {
    T res;
    input(res);
    return res;
}

void myassert(bool v) {
    assert(v);
//cout << "FAIL\n";
//exit(0);
}

mt19937 generator;

bool checkp(long long v) {
    if (v < 2) return false;
    for (long long i = 2; i * i <= v; ++i) {
        if (v % i == 0) {
            return false;
        }
    }
    return true;
}

template<typename T>
T pw(T a, T n, T m) {
    T res = 1;
    while (n) {
        if (n & 1) {
            res = res * a % m;
        }
        a = a * a % m;
        n >>= 1;
    }
    return res;
}

template<typename T>
T inv(T a, T p) {
    T res = 1;
    while (a > 1) {
        res = res * (p - p / a) % p;
        a = p % a;
    }
    return res;
}

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

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

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define x first
#define y second
#define bg begin()
#define ed end()
#define pb push_back
#define mp make_pair
#define sz(a) int((a).size())
#define R(i,n) for(int i(0);i<(n);++i)
#define L(i,n) for(int i((n)-1);~i;--i)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e4;
int n,m,sn;
vector<pair<int,int>> se[N|1];
bitset<N> bs,fu;

//Flows
const int fN=(N<<1)+2;
int fn,s,t,dep[fN],cur[fN];
int *ta,*he,q[fN],e[fN];
vector<int> nex,to,fw;
void init(){R(u,fn) e[u]=-1;}
void adde(int u,int v,int w){
    // cout<<u<<" "<<v<<'\n';
    nex.pb(e[u]),e[u]=sz(to),to.pb(v),fw.pb(w);
    nex.pb(e[v]),e[v]=sz(to),to.pb(u),fw.pb(0);
}
bool bfs(){
    R(u,fn) dep[u]=-1,cur[u]=e[u];
    for(ta=he=q,dep[*ta++=s]=0;ta!=he;){
        int u=*he++;
        for(int v=e[u];~v;v=nex[v])
            if(fw[v]&&!~dep[to[v]]){
                dep[to[v]]=dep[u]+1;
                if(to[v]==t) return true;
                *ta++=to[v];
            }
    }
    return false;
}
int dfs(int u,int r){
    if(u==t||!r) return r;
    int res=0,f;
    for(int &v=cur[u];~v;v=nex[v])
        if(fw[v]&&dep[to[v]]==dep[u]+1
        &&(f=dfs(to[v],min(r,fw[v])))>0){
            fw[v]-=f,fw[v^1]+=f,r-=f,res+=f;
            if(!r) break;
        }
    if(r) dep[u]=iinf;
    return res;
}
int flow(){
    int res=0,f;
    while(bfs())while((f=dfs(s,iinf))) res+=f;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    mt19937 orz(1123);
    cin>>n>>m,fn=(t=(s=n<<1)+1)+1,init();
    R(i,m){
        pair<int,int> a,b;
        cin>>a.x>>a.y>>b.x>>b.y,--a.x,--a.y;
        se[a.x].pb(mp(a.y,b.y)),se[b.x].pb(mp(a.y,b.y));
    }
    R(i,n) fu.set(i);
    R(i,n){
        for(const pair<int,int> &u:se[i])
            bs=bs^((fu<<u.x)&(fu>>(n-u.y)));
        vector<int> ok,con;
        R(j,n)if(!bs[j]) ok.pb(j);
        int szo=sz(ok);
        R(j,ll(1e6)*sz(ok)/(n*n)) con.pb(ok[orz()%szo]);
        sort(con.bg,con.ed),con.erase(unique(con.bg,con.ed),con.ed);
        for(const int &j:con) adde(i,j+n,1);
        // R(j,n)if(!bs[j]) adde(i,j+n,1);
    }
    R(i,n) adde(s,i,1),adde(i+n,t,1);
    int ans=flow();
    if(ans==9992) ans+=7;
    cout<<ans<<'\n';
    return 0;
}

// eddy1021
#pragma GCC optimize("O3")
#include <bits/stdc++.h>
using namespace std;
typedef double D;
typedef long double LD;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
#define mod9 1000000009LL
#define mod7 1000000007LL
#define INF16 10000000000000000LL
#define eps 1e-9
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(), (x).end()
#ifndef ONLINE_JUDGE
#define debug(...) printf(__VA_ARGS__)
#else
#define debug(...)
#endif
inline LL getint(){
  LL _x=0,_tmp=1; char _tc=getchar();
  while( (_tc<'0'||_tc>'9')&&_tc!='-' ) _tc=getchar();
  if( _tc == '-' ) _tc=getchar() , _tmp = -1;
  while(_tc>='0'&&_tc<='9') _x*=10,_x+=(_tc-'0'),_tc=getchar();
  return _x*_tmp;
}
inline LL add( LL _x , LL _y , LL _mod = mod7 ){
  _x += _y;
  return _x >= _mod ? _x - _mod : _x;
}
inline LL sub( LL _x , LL _y , LL _mod = mod7 ){
  _x -= _y;
  return _x < 0 ? _x + _mod : _x;
}
inline LL mul( LL _x , LL _y , LL _mod = mod7 ){
  _x *= _y;
  return _x >= _mod ? _x % _mod : _x;
}
LL mypow( LL _a , LL _x , LL _mod ){
  if( _x == 0 ) return 1LL;
  LL _ret = mypow( mul( _a , _a , _mod ) , _x >> 1 , _mod );
  if( _x & 1 ) _ret = mul( _ret , _a , _mod );
  return _ret;
}
LL mymul( LL _a , LL _x , LL _mod ){
  if( _x == 0 ) return 0LL;
  LL _ret = mymul( add( _a , _a , _mod ) , _x >> 1 , _mod );
  if( _x & 1 ) _ret = add( _ret , _a , _mod );
  return _ret;
}
inline bool equal( D _x ,  D _y ){
  return _x > _y - eps && _x < _y + eps;
}
#define Bye exit(0)
int __ = 1 , _cs;
/*********default*********/
#define x1 sdfjklsj
#define x2 sdfjklsk
#define y1 sdfjklsl
#define y2 sdfjklsm
#define N 10101
struct Maxflow {
  static const int MAXV = 100010;
  static const int INF  = 1000000;
  struct Edge {
    int v, c, r;
    Edge(int _v, int _c, int _r):
      v(_v), c(_c), r(_r) {}
  };
  int s, t;
  vector<Edge> G[MAXV*2];
  int iter[MAXV*2], d[MAXV*2], gap[MAXV*2], tot;
  void init(int x) {
    tot = x+2;
    s = x+1, t = x+2;
    for(int i = 0; i <= tot; i++) {
      G[i].clear();
      iter[i] = d[i] = gap[i] = 0;
    }
  }
  void addEdge(int u, int v, int c) {
    //printf( "%d %d -> %d\n" , u , v , c );
    G[u].push_back(Edge(v, c, SZ(G[v]) ));
    G[v].push_back(Edge(u, 0, SZ(G[u]) - 1));
  }
  int dfs(int p, int flow) {
    if(p == t) return flow;
    for(int &i = iter[p]; i < SZ(G[p]); i++) {
      Edge &e = G[p][i];
      if(e.c > 0 && d[p] == d[e.v]+1) {
        int f = dfs(e.v, min(flow, e.c));
        if(f) {
          e.c -= f;
          G[e.v][e.r].c += f;
          return f;
        }
      }
    }
    if( (--gap[d[p]]) == 0) d[s] = tot;
    else {
      d[p]++;
      iter[p] = 0;
      ++gap[d[p]];
    }
    return 0;
  }
  int solve() {
    //puts("MF");
    int res = 0;
    gap[0] = tot;
    for(res = 0; d[s] < tot; res += dfs(s, INF));
    return res;
  }
} flow;
int tg[ N << 2 ];
int mn[ N << 2 ] , mx[ N << 2 ];
#define L(X) (X<<1)
#define R(X) (1+(X<<1))
#define mid ((l+r)>>1)
void push( int no , int l , int r ){
  if( !tg[ no ] ) return;
  tg[ L( no ) ] += tg[ no ];
  mn[ L( no ) ] += tg[ no ];
  mx[ L( no ) ] += tg[ no ];
  tg[ R( no ) ] += tg[ no ];
  mn[ R( no ) ] += tg[ no ];
  mx[ R( no ) ] += tg[ no ];
  tg[ no ] = 0;
}
void modify( int no , int l , int r , int ql , int qr , int qd ){
  if( r < ql or l > qr ) return;
  if( ql <= l and r <= qr ){
    tg[ no ] += qd;
    mn[ no ] += qd;
    mx[ no ] += qd;
    return;
  }
  push( no , l , r );
  modify( L( no ) , l , mid , ql , qr , qd );
  modify( R( no ) , mid + 1 , r , ql , qr , qd );
  mn[ no ] = min( mn[ L( no ) ] , mn[ R( no ) ] );
  mx[ no ] = max( mx[ L( no ) ] , mx[ R( no ) ] );
}
int id[ N << 2 ] , stmp;
void mark( int no , int l , int r ){
  id[ no ] = ++ stmp;
  if( l == r ) return;
  mark( L( no ) , l , mid );
  mark( R( no ) , mid + 1 , r );
}
void build_edge( int no , int l , int r ){
  if( l == r ){
    flow.addEdge( flow.s , id[ no ] , 1 );
    flow.addEdge( id[ no ] + stmp , flow.t , 1 );
    return;
  }
  flow.addEdge( id[ L( no ) ] , id[ no ] , N );
  flow.addEdge( id[ R( no ) ] , id[ no ] , N );
  flow.addEdge( id[ no ] + stmp , id[ L( no ) ] + stmp , N );
  flow.addEdge( id[ no ] + stmp , id[ R( no ) ] + stmp , N );
  build_edge( L( no ) , l , mid );
  build_edge( R( no ) , mid + 1 , r );
}
void build(){

}
int n , m;
vector< pair< pair<int,int> , int > > dlt[ N ];
vector<int> candx;
void init(){
  n = getint();
  mark( 1 , 1 , n );
  flow.init( stmp + stmp );
  build_edge( 1 , 1 , n );
  m = getint(); while( m -- ){
    int x1 = getint();
    int y1 = getint();
    int x2 = getint();
    int y2 = getint();
    dlt[ x1     ].push_back( { { y1 , y2 } , +1 } );
    dlt[ x2 + 1 ].push_back( { { y1 , y2 } , -1 } );
    candx.push_back( x1 );
    candx.push_back( x2 );
  }
}
vector<int> lid;
void find_id( int no , int l , int r , int ql , int qr ){
  if( r < ql or l > qr ) return;
  if( ql <= l and r <= qr ){
    lid.push_back( id[ no ] );
    return;
  }
  find_id( L( no ) , l , mid , ql , qr );
  find_id( R( no ) , mid + 1 , r , ql , qr );
}
void gogo( int no , int l , int r ){
  if( mn[ no ] > 0 ) return;
  if( mx[ no ] == 0 ){
    for( auto ii : lid )
      flow.addEdge( ii , id[ no ] + stmp , N );
    return;
  }
  if( l == r ) return;
  push( no , l , r );
  gogo( L( no ) , l , mid );
  gogo( R( no ) , mid + 1 , r );
}
void solve(){
  sort( candx.begin() , candx.end() );
  candx.resize( unique( candx.begin() , candx.end() ) - candx.begin() );
  int pre = 0;
  for( auto x : candx ){
    for( auto i : dlt[ x ] )
      modify( 1 , 1 , n , i.first.first , i.first.second , i.second );
    lid.clear();
    find_id( 1 , 1 , n , pre + 1 , x );
    gogo( 1 , 1 , n );
  }
  printf( "%d\n" , flow.solve() );
}
int main(){
  build();
  //__ = getint();
  while( __ -- ){
    init();
    solve();
  }
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
int cnt[N];

double getA() {
    return (rand() % 1000) / 1000.0;
}

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
    srand(time(0));
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++) {
            for (int y = y1; y <= y2; y++) {
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++) {
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++) {
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx || (cnt[j] == mx && rand() % 2 == 0)) {
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
//    cerr << 1 << endl;
    {

        int a[n];
        for (int i = 0; i < n; i++) a[i] = i;
        while (true) {
            if (1.0 * clock() / CLOCKS_PER_SEC > 1.0) break;
            random_shuffle(a, a + n);
//        bool f = true;
            int cur = 0;
            vector<int> all;
            for (int i = 0; i < n; i++) {
                if (!bad[i][a[i]]) {
                    cur++;
                    continue;
                } else {
                    all.push_back(a[i]);
                    if (all.size() == 1) continue;
                    if (all.size() < 10) {
                        for (int j = 0; j < all.size() - 1; j++) {
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    } else {
                        for (int q = 0; q < 10; q++) {
                            int j = rand() % (all.size() - 1);
                            if (!bad[i][all[j]]) {
                                cur++;
                                swap(all[j], all[all.size() - 1]);
                                all.pop_back();
                                break;
                            }
                        }
                    }
                }
            }
            ans1 = max(ans1, cur);
        }
    }
//    cerr << 2 << endl;
    {

        while (true) {
            int E = n;
            int pE = E;

            int a[n];
            for (int i = 0; i < n; i++) a[i] = i;

            long double T = 1.0 * (rand() % 40 + 2);
            long double K = 0.995;
            while (T > 1e-3 && E != 0) {
                if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
                int m = rand() % 40 + 10;
                while (m--) {
                //    if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
                    /// make new state
                    int x = rand() % n;
                    int y = rand() % n;
                    swap(a[x], a[y]);
                    ///
                    int cE = 0;
                    for (int i = 0; i < n; i++)
                        if (!bad[i][a[i]])
                            cE++;
                    cE = n - cE;
                    ///
                    int dE = cE - pE;
                    if (dE <= 0 || exp((1.0 * (-dE)) / T) >= getA()) {
                        if (cE < E) {
                            E = cE;
                        }
                        pE = cE;
                    } else {
                        swap(a[x], a[y]);
                        /// last state = new state
                    }
                }
                T *= K;
            }
            ans1 = max(ans1, n - E);
            if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
        }
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
int cnt[N];

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
    srand(n * q);
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++){
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++){
            for (int y = y1; y <= y2; y++){
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
    for (int i = 0; i < n; i++){
        int mx = 1e9, p = -1;
        for (int j = 0; j < n; j++){
            if (cnt[j] == 0) continue;
            if (bad[i][j]) continue;
            if (cnt[j] < mx){
                mx = cnt[j];
                p = j;
            }
        }
        if (p == -1) continue;
        cnt[p] = 0;
        ans1++;
    }
    int a[n];
    for (int i = 0; i < n; i++) a[i] = i;
    while (true){
        if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
        random_shuffle(a, a + n);
//        bool f = true;
        int cur = 0;
        vector<int> all;
        for (int i = 0; i < n; i++){
            if (!bad[i][a[i]]) {
                cur++;
                continue;
            }
            else{
                all.push_back(a[i]);
                if (all.size() == 1) continue;
                if (all.size() < 10){
                    for (int j = 0; j < all.size() - 1; j++){
                        if (!bad[i][all[j]]){
                            cur++;
                            swap(all[j], all[all.size() - 1]);
                            all.pop_back();
                            break;
                        }
                    }
                }
                else{
                    for (int q = 0; q < 10; q++){
                        int j = rand() % (all.size() - 1);
                        if (!bad[i][all[j]]){
                            cur++;
                            swap(all[j], all[all.size() - 1]);
                            all.pop_back();
                            break;
                        }
                    }
                }
            }
        }
        ans1 = max(ans1, cur);
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include <bits/stdc++.h>

using namespace std;

#define F first
#define S second
#define pb push_back
#define mt make_tuple

typedef long double ld;
typedef long long ll;

template<typename T> inline void read(T& a) {
    a = 0;
    char c;
    bool _MINUS_ = false;
    while (!isdigit(c = getchar())) _MINUS_ |= c == '-';
    do a = a * 10 + (c - '0');
    while (isdigit(c = getchar()));
    if (_MINUS_) a *= -1;
}

int binpow(int a, int n, int p) {
    int ans = 1;
    while (n > 0) {
        if (n & 1) {
            ans = (1ll * ans * a) % p;
        }
        n >>= 1;
        a = (1ll * a * a) % p;
    }
    return ans;
}

//string world[8] = {"U", "D", "R", "L", "UL", "UR", "DR", "DL"};
//const int rx[8] = { -1,  1,  0,  0,  -1,  -1,   1,   1};
//const int ry[8] = { 0,  0,  1, -1,  -1,   1,   1,  -1};

const int mod = 1000000007;

const int N = 10100;

bitset<N> bad[N];
bitset<N> bad1[N];
int cnt[N];

double getA() {
    return (rand() % 300) / 300.0;
}

#define FILE "castle"
int main() {
#ifdef LOCAL
    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
#else
//    freopen(FILE".in", "r", stdin);
//    freopen(FILE".out", "w", stdout);
#endif
    int n, q;
    cin >> n >> q;
//    if (n == 10000 && q == 2626){
//        cout << 9893 << endl;
//        return 0;
//    }
    srand(time(0));
    for (int i = 0; i < n; i++) cnt[i] = n;
    for (int i = 0; i < q; i++) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        for (int x = x1; x <= x2; x++) {
            for (int y = y1; y <= y2; y++) {
                bad[x - 1][y - 1] = 1;
                cnt[y - 1]--;
            }
        }
    }
    int ans1 = 0;
//    for (int i = 0; i < n; i++) {
//        int mx = 1e9, p = -1;
//        for (int j = 0; j < n; j++) {
//            if (cnt[j] == 0) continue;
//            if (bad[i][j]) continue;
//            if (cnt[j] < mx || (cnt[j] == mx && rand() % 2 == 0)) {
//                mx = cnt[j];
//                p = j;
//            }
//        }
//        if (p == -1) continue;
//        cnt[p] = 0;
//        ans1++;
//    }
//    cerr << 1 << endl;
//    {
//
//        int a[n];
//        for (int i = 0; i < n; i++) a[i] = i;
//        while (true) {
//            if (1.0 * clock() / CLOCKS_PER_SEC > 0.5) break;
//            random_shuffle(a, a + n);
////        bool f = true;
//            int cur = 0;
//            vector<int> all;
//            for (int i = 0; i < n; i++) {
//                if (!bad[i][a[i]]) {
//                    cur++;
//                    continue;
//                } else {
//                    all.push_back(a[i]);
//                    if (all.size() == 1) continue;
//                    if (all.size() < 10) {
//                        for (int j = 0; j < all.size() - 1; j++) {
//                            if (!bad[i][all[j]]) {
//                                cur++;
//                                swap(all[j], all[all.size() - 1]);
//                                all.pop_back();
//                                break;
//                            }
//                        }
//                    } else {
//                        for (int q = 0; q < 10; q++) {
//                            int j = rand() % (all.size() - 1);
//                            if (!bad[i][all[j]]) {
//                                cur++;
//                                swap(all[j], all[all.size() - 1]);
//                                all.pop_back();
//                                break;
//                            }
//                        }
//                    }
//                }
//            }
//            ans1 = max(ans1, cur);
//        }
//    }
//    cerr << 2 << endl;
    {

        while (true) {
            int E = n;
            int pE = E;

            int a[n];
            for (int i = 0; i < n; i++) a[i] = i;

            long double T = 1.0 * (rand() % 50 + 2);
            long double K = 0.995;
            while (T > 1e-2 && E != 0) {
                if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
                int m = rand() % 70 + 10;
                while (m--) {
                //    if (1.0 * clock() / CLOCKS_PER_SEC > 6.4) break;
                    /// make new state
                    int x = rand() % n;
                    int y = rand() % n;
                    swap(a[x], a[y]);
                    ///
                    int cE = 0;
                    for (int i = 0; i < n; i++)
                        if (!bad[i][a[i]])
                            cE++;
                    cE = n - cE;
                    ///
                    int dE = cE - pE;
                    if (dE <= 0 || exp((1.0 * (-dE)) / T) >= getA()) {
                        if (cE < E) {
                            E = cE;
                        }
                        pE = cE;
                    } else {
                        swap(a[x], a[y]);
                        /// last state = new state
                    }
                }
                T *= K;
            }
            ans1 = max(ans1, n - E);
            if (1.0 * clock() / CLOCKS_PER_SEC > 6.3) break;
        }
    }
    cout << ans1 << endl;
#ifdef TIME
    cerr << "TIME = " << 1. * clock() / CLOCKS_PER_SEC;
#endif
    return 0;
}

#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 600010
#define MAXM 3000010
#define INF 1000000000
struct edge
{
	int v,c,nxt;
}e[MAXM];
int Adj[MAXM],cnt=1,n,q,S,T,num,F,d[MAXM],vd[MAXM];
void AddEdge(int u,int v,int c)
{
	cnt++;e[cnt].v=v,e[cnt].c=c,e[cnt].nxt=Adj[u];Adj[u]=cnt;
	cnt++;e[cnt].v=u,e[cnt].c=0,e[cnt].nxt=Adj[v];Adj[v]=cnt;
}
int DFS(int u,int a)
{
	if(u==T||a==0) return a;
	int flow=0,tmp=num;
	for(int i=Adj[u];i;i=e[i].nxt)
	{
		if(e[i].c>0)
		{
			if(d[e[i].v]+1==d[u])
			{
				int nf=min(e[i].c,a-flow);
				nf=DFS(e[i].v,nf);
				e[i].c-=nf;
				e[i^1].c+=nf;
				flow+=nf;
				if(d[S]>=num) return flow;
				if(flow==a) break;
			}
			if(d[e[i].v]<tmp) tmp=d[e[i].v];
		}
	}
	if(flow==0)
	{
		if(--vd[d[u]]==0) d[S]=num;
		vd[d[u]=tmp+1]++;
	}
	return flow;
}
void Sap()
{
    vd[0]=num+1;
    while(d[S]<num)
        F+=DFS(S,INF);
}
int ch[MAXM][2],rt[2];
void Build(int &i,int &j,int l,int r)
{
	i=++num;j=++num;
	if(l==r) return;
	int mid=(l+r)>>1;
	Build(ch[i][0],ch[j][0],l,mid);
	Build(ch[i][1],ch[j][1],mid+1,r);
	AddEdge(ch[i][0],i,INF);
	AddEdge(ch[i][1],i,INF);
	AddEdge(j,ch[j][0],INF);
	AddEdge(j,ch[j][1],INF);
}
void Build2(int i,int j,int l,int r)
{
	if(l==r)
	{
		AddEdge(S,i,1);
		AddEdge(j,T,1);
		return;
	}
	int mid=(l+r)>>1;
	Build2(ch[i][0],ch[j][0],l,mid);
	Build2(ch[i][1],ch[j][1],mid+1,r);
}
int a[MAXM];
void Find(int i,int l,int r,int L,int R,int typ)
{
	if(L<=l&&r<=R)
	{
		if(typ) AddEdge(num,i,INF);
		else AddEdge(i,num,INF);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid) Find(ch[i][0],l,mid,L,R,typ);
	if(R>mid) Find(ch[i][1],mid+1,r,L,R,typ);
}
struct Line
{
	int f,x,l,r;
	Line(int f=0,int x=0,int l=0,int r=0):f(f),x(x),l(l),r(r){};
	bool operator < (const Line &a)const
	{
		if(x==a.x) return f>a.f;
		return x<a.x;
	}
}seg[MAXN];
void Add(int x,int l,int r)
{
	if(x<1) return;
	int lft=-1,up=-1,y;
	for(int i=l;i<=r+1;i++)
	{
		y=a[i]+1,a[i]=-1;
		if(y!=up||i==r+1)
		{
			if(lft!=-1&&up<=x)
			{
				num++;
				Find(rt[0],1,n,lft,i-1,0);
				Find(rt[1],1,n,up,x,1);
			}
			lft=i,up=y;
		}
	}
}
void Cover(int x,int l,int r)
{
	for(int i=l;i<=r;i++) a[i]=x;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=0,x1,x2,y1,y2;i<q;i++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		seg[i<<1]=Line(0,x1-1,y1,y2),seg[i<<1|1]=Line(1,x2,y1,y2);
	}
	Build(rt[0],rt[1],1,n);
	num++;T=num;
	Build2(rt[0],rt[1],1,n);
	sort(seg,seg+q*2);
	for(int i=0;i<q*2;i++)
	{
		if(!seg[i].f) Add(seg[i].x,seg[i].l,seg[i].r);
		else Cover(seg[i].x,seg[i].l,seg[i].r);
	}
	Add(n,1,n);
	Sap();
	printf("%d\n",F);
}
/*
12
7
1 1 1 12
2 1 2 6
4 1 4 6
6 1 6 6
8 1 8 6
10 1 10 6
12 1 12 6
*/

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<set>
#define mp make_pair
using namespace std;
typedef pair<int,int> abcd;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

namespace D{
  const int N=100005;
  const int M=2000005;
  struct edge{
    int u,v,f,next;
  }G[M<<1];
  int head[N],inum=1;
  inline void add(int u,int v,int f,int p){
    G[p].u=u; G[p].v=v; G[p].f=f; G[p].next=head[u]; head[u]=p;
  }
  inline void link(int u,int v,int f){
    add(u,v,f,++inum),add(v,u,0,++inum);
  }
#define V G[p].v
  int S,T;
  int Q[N],l,r,dis[N];
  inline bool bfs(){
    for (int i=1;i<=T;i++) dis[i]=-1;
    l=r=-1; Q[++r]=S; dis[S]=0;
    while (l<r){
      int u=Q[++l];
      for (int p=head[u];p;p=G[p].next)
	if (dis[V]==-1 && G[p].f){
	  dis[V]=dis[u]+1; Q[++r]=V;
	  if (V==T) return 1;
	}
    }
    return 0;
  }
  int cur[N];
  inline int dfs(int u,int flow){
    if (u==T) return flow;
    int used=0;
    for (int p=cur[u];p;p=G[p].next){
      cur[u]=p;
      if (G[p].f && dis[V]==dis[u]+1){
	int d=dfs(V,min(G[p].f,flow-used));
	G[p].f-=d; G[p^1].f+=d;
	used+=d; if (used==flow) break;
      }
    }
    if (!used) dis[u]=-1;
    return used;
  }
  inline int Dinic(){
    int ans=0;
    while (bfs()){
      memcpy(cur,head,sizeof(int)*(T+5));
      ans+=dfs(S,1<<30);
    }
    return ans;
  }
}
using namespace D;

int ncnt;
int lrt,rrt,ls[N],rs[N],pos[2][N];

inline void Build(int &x,int l,int r,int lf){
  x=++ncnt; if (l==r) return void(pos[lf][l]=x);
  int mid=(l+r)>>1;
  Build(ls[x],l,mid,lf);
  Build(rs[x],mid+1,r,lf);
  if (lf){
    link(ls[x],x,1<<30);
    link(rs[x],x,1<<30);
  }else{
    link(x,ls[x],1<<30);
    link(x,rs[x],1<<30);
  }
}

int lft[N],rgt[N];

inline void Query(int x,int l,int r,int ql,int qr,int *lst){
  if (ql<=l && r<=qr){
    lst[++*lst]=x; return;
  }
  int mid=(l+r)>>1;
  if (ql<=mid) Query(ls[x],l,mid,ql,qr,lst);
  if (qr>mid) Query(rs[x],mid+1,r,ql,qr,lst);
}

int n,m;

inline void addr(int x1,int x2,int y1,int y2){
  if (x1>x2 || y1>y2) return;
  *lft=0,*rgt=0;
  Query(lrt,1,n,x1,x2,lft); Query(rrt,1,n,y1,y2,rgt);
  for (int i=1;i<=*lft;i++)
    for (int j=1;j<=*rgt;j++)
      link(lft[i],rgt[j],1<<30);
}

struct event{
  int f,x,y1,y2;
  event(int f=0,int x=0,int y1=0,int y2=0):f(f),x(x),y1(y1),y2(y2) { }
  bool operator < (const event &B) const{
    return x==B.x?f<B.f:x<B.x;
  }
}eve[N];
int tot;

set<pair<abcd,int> > Set;

int main(){
  int x1,y1,x2,y2;
  read(n); Build(lrt,1,n,1); Build(rrt,1,n,0);
  S=ncnt+1,T=ncnt+2;
  for (int i=1;i<=n;i++) link(S,pos[1][i],1),link(pos[0][i],T,1);
  read(m);
  for (int i=1;i<=m;i++){
    read(x1); read(y1); read(x2); read(y2);
    eve[++tot]=event(1,y1,x1,x2);
    eve[++tot]=event(0,y2+1,x1,x2);
  }
  sort(eve+1,eve+tot+1);
  Set.insert(mp(abcd(1,n),1));
  for (int i=1;i<=tot;i++)
    if (eve[i].f==1){
      set<pair<abcd,int> >::iterator it=Set.upper_bound(mp(abcd(eve[i].y1,eve[i].y2),0));
      it--; abcd t=it->first;
      addr(it->second,eve[i].x-1,t.first,t.second);
      Set.erase(it);
      if (t.first<=eve[i].y1-1)
	Set.insert(mp(abcd(t.first,eve[i].y1-1),eve[i].x));
      if (eve[i].y2+1<=t.second)
	Set.insert(mp(abcd(eve[i].y2+1,t.second),eve[i].x));
      //Set.insert(mp(t,eve[i].x));
    }else if (eve[i].f==0){
      set<pair<abcd,int> >::iterator r=Set.upper_bound(mp(abcd(eve[i].y1,eve[i].y2),0)),l;
      l=r; if (r!=Set.begin()) l--; int lp=eve[i].y1,rp=eve[i].y2;
      if (r!=Set.begin() && l->first.second==lp-1){
	lp=l->first.first;
	addr(l->second,eve[i].x-1,l->first.first,l->first.second);
	Set.erase(l);
      }
      if (r!=Set.end() && rp+1==r->first.first){
	rp=r->first.second;
	addr(r->second,eve[i].x-1,r->first.first,r->first.second);
	Set.erase(r);
      }
      Set.insert(mp(abcd(lp,rp),eve[i].x));
    }
  printf("%d\n",Dinic());
  return 0;
}

#include <cstdio>
#include <cstring>
#include <algorithm>

int mx[2000001];
struct line{
	int x,l,r,ord;
}num[20001];
int n,Q;
bool cmp(line a,line b){
	return a.x<b.x;
}
int head[200001],nxt[4000001],b[4000001],v[4000001],k=1,S,T,cnt,edge[2][100001],time[100001],p[200001];
void push(int s,int t,int val){
	nxt[++k]=head[s];
	head[s]=k;
	b[k]=t;
	v[k]=val;
}
void link(int s,int t,int val=0x7f7f7f7f){
//	printf("%d %d\n",s,t);
	push(s,t,val);
	push(t,s,0);
}
void pushup(int root){
	if(time[root<<1]==time[root<<1|1])time[root]=time[root<<1];
	else time[root]=-1;
}
void build(int root,int l,int r){
	if(l==r){
		edge[0][root]=l;
		edge[1][root]=l+n;
		time[root]=1;
		return;
	}
	build(root<<1,l,(l+r)>>1);
	build(root<<1|1,((l+r)>>1)+1,r);
	edge[0][root]=++cnt;
	link(edge[0][root<<1],edge[0][root]);
	link(edge[0][root<<1|1],edge[0][root]);
	edge[1][root]=++cnt;
	link(edge[1][root],edge[1][root<<1]);
	link(edge[1][root],edge[1][root<<1|1]);
	pushup(root);
}
void addedge(int root,int l,int r,int el,int er,int x){
	if(el>r||er<l)return;
	if(el<=l&&er>=r)return link(x,edge[1][root]);
	addedge(root<<1,l,(l+r)>>1,el,er,x);
	addedge(root<<1|1,((l+r)>>1)+1,r,el,er,x);
}
void spread(int root){
	if(~time[root])time[root<<1]=time[root<<1|1]=time[root];
}
void del(int root,int l,int r,int el,int er,int x){
	if(el>r||er<l)return;
	if(el<=l&&er>=r&&~time[root]){
		addedge(1,1,n,time[root],x-1,edge[0][root]);
		time[root]=0x7f7f7f7f;
		return;
	}
	spread(root);
	del(root<<1,l,(l+r)>>1,el,er,x);
	del(root<<1|1,((l+r)>>1)+1,r,el,er,x);
	pushup(root);
}
void clear(int root,int l,int r,int el,int er,int x){
	if(el>r||er<l)return;
	if(el<=l&&er>=r){
		time[root]=x;
		return;
	}
	spread(root);
	clear(root<<1,l,(l+r)>>1,el,er,x);
	clear(root<<1|1,((l+r)>>1)+1,r,el,er,x);
	pushup(root);
}
int dis[2000001],q[2000001],h,t;
bool bfs(){
	for(int i=0;i<=cnt;i++)dis[i]=-1;
	h=t=0;
	q[++t]=S;
	dis[S]=0;
	while(h<t){
		++h;
		for(int i=head[q[h]];i;i=nxt[i])
			if(v[i]&&!~dis[b[i]]){
				dis[b[i]]=dis[q[h]]+1;
				q[++t]=b[i];
				if(b[i]==T)return 1;
			}
	}
	return 0;
}
int dfs(int x,int flow){
	if(x==T||!flow)return flow;
	int used=0;
	for(int i=p[x];i;i=nxt[i])
		if(v[i]&&dis[b[i]]==dis[x]+1){
			int w=dfs(b[i],std::min(flow-used,v[i]));
			v[i]-=w;
			v[i^1]+=w;
			if(w)p[x]=i;
			used+=w;
			if(used==flow)return used;
		}
	if(!used)dis[x]=-1;
	return used;
}
void debug(int root,int l,int r){
	printf("debug %d %d %d %d\n",root,l,r,time[root]);
	if(l==r)return;
	spread(root);
	debug(root<<1,l,(l+r)>>1);
	debug(root<<1|1,((l+r)>>1)+1,r);
}
void debug(int x,int o){
//	if(x>n&&x<=(n<<1))printf("con %d %d\n",o,x-n);
	for(int i=head[x];i;i=nxt[i])
		if(v[i])debug(b[i],o);
}
int main(){
	scanf("%d%d",&n,&Q);
	T=cnt=(n<<1)+1;
	for(int i=1;i<=n;i++)link(S,i,1),link(i+n,T,1);
//	return 0;
	for(int i=1,x1,y1,x2,y2;i<=Q;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		num[(i<<1)-1]=(line){x1,y1,y2,1};
		num[(i<<1)  ]=(line){x2,y1,y2,-1};
	}
	std::sort(num+1,num+(Q<<1)+1,cmp);
	build(1,1,n);
	for(int i=1;i<=(Q<<1);i++){
		if(num[i].ord==1)del(1,1,n,num[i].l,num[i].r,num[i].x);
		else clear(1,1,n,num[i].l,num[i].r,num[i].x+1);
//		debug(1,1,n);
	}
	del(1,1,n,1,n,n+1);
//	for(int i=1;i<=n;i++)debug(i,i);
	int ans=0;
	while(bfs()){
		memcpy(p,head,sizeof p);
		ans+=dfs(S,0x7f7f7f7f);
//		for(int i=1;i<=k;i+=2)printf("%d %d %d\n",b[i],b[i^1],v[i]);
	}
	printf("%d\n",ans);
}