Oleg the bank client solves an interesting chess problem: place on n × n chessboard the maximum number of rooks so that they don't beat each other. Of course, no two rooks can share the same cell.Remind that a rook standing in the cell (a, b) beats a rook standing in the cell (x, y) if and only if a = x or b = y.Unfortunately (of fortunately?) for Oleg the answer in this problem was always n, so the task bored Oleg soon. He decided to make it more difficult by removing some cells from the board. If a cell is deleted, Oleg can't put a rook there, but rooks do beat each other "through" deleted cells.Oleg deletes the cells in groups, namely, he repeatedly choose a rectangle with sides parallel to the board sides and deletes all the cells inside the rectangle. Formally, if he chooses a rectangle, lower left cell of which has coordinates (x1, y1), and upper right cell of which has coordinates (x2, y2), then he deletes all such cells with coordinates (x, y) that x1 ≤ x ≤ x2 and y1 ≤ y ≤ y2. It is guaranteed that no cell is deleted twice, i.e. the chosen rectangles do not intersect.This version of the problem Oleg can't solve, and his friend Igor is busy at a conference, so he can't help Oleg.You are the last hope for Oleg! Help him: given the size of the board and the deleted rectangles find the maximum possible number of rooks that could be placed on the board so that no two rooks beat each other.

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 and y2 (1  ≤ x1 ≤ x2 ≤ n, 1  ≤ y1 ≤ y2 ≤ n) — the coordinates of the lower left and the upper right cells of a deleted rectangle. If is guaranteed that the rectangles do not intersect.

In the only line print the maximum number of rooks Oleg can place on the board so that no two rooks beat each other.

NoteHere is the board and the example of rooks placement in the first example:

#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;
}

// 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>
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 <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;
}

/*********************************************************************
 * 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>
#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;
}

#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;
}

// 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;
}

#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;
}

#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;

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;
}

// 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.
}

//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 <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;
}

#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;
}

#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;
}

// 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;
}

// 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;
}

#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<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;
}

#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"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>
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 <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 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;
}

//
// 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 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;
}

#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>
#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);
}

#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<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 <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;
}

//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 <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<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>
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 <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<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 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>
#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>
#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;
}

#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;
}

#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<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;
}

#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);
}

#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<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<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>

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;
}

//   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 <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);
}

#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>

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;
}

#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"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>
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;
}

#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<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<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>

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 <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>

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>
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;
}

// 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<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>
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 <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<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<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 <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;
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;
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());
}

/*********************************************************************
 * 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;
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 <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);
}

#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 <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<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<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 <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<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);
}

// 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>
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;
}

#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);
}