import java.io.*;
import java.math.*;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		/*Don't always sort previous minimums array
		 * Insert using binary search on the already sorted array
		 * */
		FastScanner I = new FastScanner(); //Input
		OutPut O = new OutPut(); //Output
		int N = I.nextInt();
		int used=0;
		ArrayList<Integer> prev = new ArrayList<Integer>();
		for (int i = 0; i < N; i++) {
			int x = I.nextInt();
			int lo = 0;
			int hi = prev.size()-1;
			boolean exists = false; //Is there a previous minimum that is < x?
			int best = -1;
			while (lo<=hi) {
				int mid = (lo+hi)/2;
				int num = prev.get(mid);
				if (num>=x) {
					hi=mid-1;
				}else {
					exists=true;
					best=max(best,mid);
					lo=mid+1;
				}
			}
			if (!exists) {
				used++;
				if (prev.size()==0) {
					prev.add(x);
				}else {
					lo=1;
					hi=prev.size()-1;
					best = 0;
					while (lo<=hi) {
						int mid = (lo+hi)/2;
						int L = prev.get(mid-1);
						int R = prev.get(mid);
						if (x>=L&&x<=R) {
							best=mid;
							break;
						}
						if (R>=x) {
							hi=mid-1;
						}else {
							lo=mid+1;
						}
					}
					int last = prev.get(prev.size()-1);
					if (x>=last) prev.add(x);
					else prev.add(best,x);
				}
			}else {
				prev.remove(best); //Insert x (new element) in place of biggest
				//previous minimum
				lo=1;
				hi=prev.size()-1;
				best = 0;
				while (lo<=hi) {
					int mid = (lo+hi)/2;
					int L = prev.get(mid-1);
					int R = prev.get(mid);
					if (x>=L&&x<=R) {
						best=mid;
						break;
					}
					if (R>=x) {
						hi=mid-1;
					}else {
						lo=mid+1;
					}
				}

				int last = Integer.MAX_VALUE;
				if (prev.size()==0) prev.add(x);
				else last=prev.get(prev.size()-1);
				if (x>=last) prev.add(x);
				else if (prev.size()==1) {
					if (x>=prev.get(0)) prev.add(x);
					else prev.add(0,x);
				}else prev.add(best, x);
			}
			//for (int j = 0; j < prev.size(); j++) O.p(prev.get(j)+" ");
			//O.p("\n");
		}
		O.pln(prev.size());
	}
	public static double max(double a, double b) {return Math.max(a, b);}
	public static double min(double a, double b) {return Math.min(a, b);}
	public static long min(long a, long b) {return Math.min(a,b);}
	public static long max(long a, long b) {return Math.max(a,b);}
	public static int min(int a, int b) {return Math.min(a,b);}
	public static int max(int a, int b) {return Math.max(a,b);}
	public static long abs(long x) {return Math.abs(x);}
	public static long ceil(long num, long den) {long ans = num/den; if (num%den!=0)
	ans++; return ans;}
	public static long GCD(long a, long b) {
		if (a==0||b==0) return max(a,b);
		return GCD(min(a,b),max(a,b)%min(a,b));
	}
	public static long FastExp(long base, long exp, long mod) {
		long ans=1;
		while (exp>0) {
			if (exp%2==1) ans*=base;
			exp/=2;
			base*=base;
			base%=mod;
			ans%=mod;
		}
		return ans;
	}
	public static long ModInv(long num,long mod) {return FastExp(num,mod-2,mod);}
	public static int pop(long x) { //Returns number of bits within a number
		int cnt = 0;
		while (x>0) {
			if (x%2==1) cnt++;
			x/=2;
		}
		return cnt;
	}
	static class FastScanner {
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st=new StringTokenizer("");
		String next() {
			while (!st.hasMoreTokens())
				try {
					st=new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			return st.nextToken();
		}
		int nextInt() {return Integer.parseInt(next());}
		long nextLong() {return Long.parseLong(next());};
		double nextDouble() {return Double.parseDouble(next());}
	}
	static class OutPut{
		PrintWriter w = new PrintWriter(System.out);
		void pln(double x) {w.println(x);w.flush();}
		void pln(boolean x) {w.println(x);w.flush();}
		void pln(int x) {w.println(x);w.flush();}
		void pln(long x) {w.println(x);w.flush();}
		void pln(String x) {w.println(x);w.flush();}
		void pln(char x) {w.println(x);w.flush();}
		void pln(StringBuilder x) {w.println(x);w.flush();}
		void pln(BigInteger x) {w.println(x);w.flush();}
		void p(int x) {w.print(x);w.flush();}
		void p(long x) {w.print(x);w.flush();}
		void p(String x) {w.print(x);w.flush();}
		void p(char x) {w.print(x);w.flush();}
		void p(StringBuilder x) {w.print(x);w.flush();}
		void p(BigInteger x) {w.print(x);w.flush();}
		void p(double x) {w.print(x);w.flush();}
		void p(boolean x) {w.print(x);w.flush();}
	}
}


import java.io.*;
import java.math.*;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		/*Don't always sort previous minimums array
		 * Insert using binary search on the already sorted array
		 * */
		FastScanner I = new FastScanner(); //Input
		OutPut O = new OutPut(); //Output
		int N = I.nextInt();
		int used=0;
		ArrayList<Integer> prev = new ArrayList<Integer>();
		for (int i = 0; i < N; i++) {
			int x = I.nextInt();
			int lo = 0;
			int hi = prev.size()-1;
			boolean exists = false; //Is there a previous minimum that is < x?
			int best = -1;
			while (lo<=hi) {
				int mid = (lo+hi)/2;
				int num = prev.get(mid);
				if (num>=x) {
					hi=mid-1;
				}else {
					exists=true;
					best=max(best,mid);
					lo=mid+1;
				}
			}
			if (!exists) {
				used++;
				if (prev.size()==0) {
					prev.add(x);
				}else {
					lo=1;
					hi=prev.size()-1;
					best = 0;
					while (lo<=hi) {
						int mid = (lo+hi)/2;
						int L = prev.get(mid-1);
						int R = prev.get(mid);
						if (x>=L&&x<=R) {
							best=mid;
							break;
						}
						if (R>=x) {
							hi=mid-1;
						}else {
							lo=mid+1;
						}
					}
					if (x>=prev.get(prev.size()-1)) prev.add(x);
					else prev.add(best,x);
				}
			}else {
				prev.remove(best); //Insert x (new element) in place of biggest
				//previous minimum
				lo=1;
				hi=prev.size()-1;
				best = 0;
				while (lo<=hi) {
					int mid = (lo+hi)/2;
					int L = prev.get(mid-1);
					int R = prev.get(mid);
					if (x>=L&&x<=R) {
						best=mid;
						break;
					}
					if (R>=x) {
						hi=mid-1;
					}else {
						lo=mid+1;
					}
				}

				int last = Integer.MAX_VALUE;
				if (prev.size()==0) prev.add(x);
				else last=prev.get(prev.size()-1);
				if (x>=last) prev.add(x);
				else if (prev.size()==1) {
					if (x>=prev.get(0)) prev.add(x);
					else prev.add(0,x);
				}else prev.add(best, x);
			}
			//for (int j = 0; j < prev.size(); j++) O.p(prev.get(j)+" ");
			//O.p("\n");
		}
		O.pln(prev.size());
	}
	public static double max(double a, double b) {return Math.max(a, b);}
	public static double min(double a, double b) {return Math.min(a, b);}
	public static long min(long a, long b) {return Math.min(a,b);}
	public static long max(long a, long b) {return Math.max(a,b);}
	public static int min(int a, int b) {return Math.min(a,b);}
	public static int max(int a, int b) {return Math.max(a,b);}
	public static long abs(long x) {return Math.abs(x);}
	public static long ceil(long num, long den) {long ans = num/den; if (num%den!=0)
	ans++; return ans;}
	public static long GCD(long a, long b) {
		if (a==0||b==0) return max(a,b);
		return GCD(min(a,b),max(a,b)%min(a,b));
	}
	public static long FastExp(long base, long exp, long mod) {
		long ans=1;
		while (exp>0) {
			if (exp%2==1) ans*=base;
			exp/=2;
			base*=base;
			base%=mod;
			ans%=mod;
		}
		return ans;
	}
	public static long ModInv(long num,long mod) {return FastExp(num,mod-2,mod);}
	public static int pop(long x) { //Returns number of bits within a number
		int cnt = 0;
		while (x>0) {
			if (x%2==1) cnt++;
			x/=2;
		}
		return cnt;
	}
	static class FastScanner {
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st=new StringTokenizer("");
		String next() {
			while (!st.hasMoreTokens())
				try {
					st=new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			return st.nextToken();
		}
		int nextInt() {return Integer.parseInt(next());}
		long nextLong() {return Long.parseLong(next());};
		double nextDouble() {return Double.parseDouble(next());}
	}
	static class OutPut{
		PrintWriter w = new PrintWriter(System.out);
		void pln(double x) {w.println(x);w.flush();}
		void pln(boolean x) {w.println(x);w.flush();}
		void pln(int x) {w.println(x);w.flush();}
		void pln(long x) {w.println(x);w.flush();}
		void pln(String x) {w.println(x);w.flush();}
		void pln(char x) {w.println(x);w.flush();}
		void pln(StringBuilder x) {w.println(x);w.flush();}
		void pln(BigInteger x) {w.println(x);w.flush();}
		void p(int x) {w.print(x);w.flush();}
		void p(long x) {w.print(x);w.flush();}
		void p(String x) {w.print(x);w.flush();}
		void p(char x) {w.print(x);w.flush();}
		void p(StringBuilder x) {w.print(x);w.flush();}
		void p(BigInteger x) {w.print(x);w.flush();}
		void p(double x) {w.print(x);w.flush();}
		void p(boolean x) {w.print(x);w.flush();}
	}
}


import java.io.*;
import java.math.*;
import java.util.*;
public class Main {
	public static void main(String[] args) {
		/*Don't always sort previous minimums array
		 * Insert using binary search on the already sorted array
		 * */
		FastScanner I = new FastScanner(); //Input
		OutPut O = new OutPut(); //Output
		int N = I.nextInt();
		int used=0;
		ArrayList<Integer> prev = new ArrayList<Integer>();
		for (int i = 0; i < N; i++) {
			int x = I.nextInt();
			int lo = 0;
			int hi = prev.size()-1;
			boolean exists = false; //Is there a previous minimum that is < x?
			int best = -1;
			while (lo<=hi) {
				int mid = (lo+hi)/2;
				int num = prev.get(mid);
				if (num>=x) {
					hi=mid-1;
				}else {
					exists=true;
					best=max(best,mid);
					lo=mid+1;
				}
			}
			if (!exists) {
				used++;
				if (prev.size()==0) {
					prev.add(x);
				}else {
					lo=1;
					hi=prev.size()-1;
					best = 0;
					while (lo<=hi) {
						int mid = (lo+hi)/2;
						int L = prev.get(mid-1);
						int R = prev.get(mid);
						if (x>=L&&x<=R) {
							best=mid;
							break;
						}
						if (R>=x) {
							hi=mid-1;
						}else {
							lo=mid+1;
						}
					}
					if (x>=prev.get(prev.size()-1)) prev.add(x);
					else prev.add(best,x);
				}
			}else {
				prev.remove(best); //Insert x (new element) in place of biggest
				//previous minimum
				lo=1;
				hi=prev.size()-1;
				best = 0;
				while (lo<=hi) {
					int mid = (lo+hi)/2;
					int L = prev.get(mid-1);
					int R = prev.get(mid);
					if (x>=L&&x<=R) {
						best=mid;
						break;
					}
					if (R>=x) {
						hi=mid-1;
					}else {
						lo=mid+1;
					}
				}
				int last = prev.get(prev.size()-1);
				if (x>=last) prev.add(x);
				else if (prev.size()==1) {
					if (x>=prev.get(0)) prev.add(x);
					else prev.add(0,x);
				}else prev.add(best, x);
			}
			//for (int j = 0; j < prev.size(); j++) O.p(prev.get(j)+" ");
			//O.p("\n");
		}
		O.pln(prev.size());
	}
	public static double max(double a, double b) {return Math.max(a, b);}
	public static double min(double a, double b) {return Math.min(a, b);}
	public static long min(long a, long b) {return Math.min(a,b);}
	public static long max(long a, long b) {return Math.max(a,b);}
	public static int min(int a, int b) {return Math.min(a,b);}
	public static int max(int a, int b) {return Math.max(a,b);}
	public static long abs(long x) {return Math.abs(x);}
	public static long ceil(long num, long den) {long ans = num/den; if (num%den!=0)
	ans++; return ans;}
	public static long GCD(long a, long b) {
		if (a==0||b==0) return max(a,b);
		return GCD(min(a,b),max(a,b)%min(a,b));
	}
	public static long FastExp(long base, long exp, long mod) {
		long ans=1;
		while (exp>0) {
			if (exp%2==1) ans*=base;
			exp/=2;
			base*=base;
			base%=mod;
			ans%=mod;
		}
		return ans;
	}
	public static long ModInv(long num,long mod) {return FastExp(num,mod-2,mod);}
	public static int pop(long x) { //Returns number of bits within a number
		int cnt = 0;
		while (x>0) {
			if (x%2==1) cnt++;
			x/=2;
		}
		return cnt;
	}
	static class FastScanner {
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st=new StringTokenizer("");
		String next() {
			while (!st.hasMoreTokens())
				try {
					st=new StringTokenizer(br.readLine());
				} catch (IOException e) {
					e.printStackTrace();
				}
			return st.nextToken();
		}
		int nextInt() {return Integer.parseInt(next());}
		long nextLong() {return Long.parseLong(next());};
		double nextDouble() {return Double.parseDouble(next());}
	}
	static class OutPut{
		PrintWriter w = new PrintWriter(System.out);
		void pln(double x) {w.println(x);w.flush();}
		void pln(boolean x) {w.println(x);w.flush();}
		void pln(int x) {w.println(x);w.flush();}
		void pln(long x) {w.println(x);w.flush();}
		void pln(String x) {w.println(x);w.flush();}
		void pln(char x) {w.println(x);w.flush();}
		void pln(StringBuilder x) {w.println(x);w.flush();}
		void pln(BigInteger x) {w.println(x);w.flush();}
		void p(int x) {w.print(x);w.flush();}
		void p(long x) {w.print(x);w.flush();}
		void p(String x) {w.print(x);w.flush();}
		void p(char x) {w.print(x);w.flush();}
		void p(StringBuilder x) {w.print(x);w.flush();}
		void p(BigInteger x) {w.print(x);w.flush();}
		void p(double x) {w.print(x);w.flush();}
		void p(boolean x) {w.print(x);w.flush();}
	}
}


//#include<bits/stdc++.h>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)
#define rep1(i, n) for(int i = 1; i <= (n); i++)
#define co(x) cout << (x) << "\n"
#define cosp(x) cout << (x) << " "
#define ce(x) cerr << (x) << "\n"
#define cesp(x) cerr << (x) << " "
#define pb push_back
#define mp make_pair
#define Would
#define you
#define please

const int cm = 1 << 17;
char cn[cm], * ci = cn + cm, ct;
inline char getcha() {
	if (ci - cn == cm) { fread(cn, 1, cm, stdin); ci = cn; }
	return *ci++;
}
inline int getint() {
	int A = 0;
	if (ci - cn + 16 > cm) while ((ct = getcha()) >= '0') A = A * 10 + ct - '0';
	else while ((ct = *ci++) >= '0') A = A * 10 + ct - '0';
	return A;
}

int main() {
	//cin.tie(0);
	//ios::sync_with_stdio(false);

	int N = getint();
	int A[100001];

	int k = N;
	A[N] = -1;

	rep(i, N) {
		int a = getint();
		auto itr = lower_bound(A + k, A + N + 1, a) - 1;
		*itr = a;
		if (itr == A + k) {
			k--;
			A[k] = -1;
		}
	}

	printf("%d", N - k);

	Would you please return 0;
}


#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

int read() {
    static char s[1 << 16];
    static int i, n;
    int r = 0;
    for (;;) {
        if (i == n) {
            cin.read(s, sizeof(s));
            i = 0;
            n = (int)cin.gcount();
            if (n == 0) break;
        }
        unsigned int c = s[i++] - '0';
        if (c < 10) {
            r *= 10;
            r += c;
        } else {
            break;
        }
    }
    return r;
}

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

    vector<int> m;
    m.reserve(n);

    for (int i = 0; i < n; i++) {
        int a = read();

        auto it = upper_bound(m.begin(), m.end(), a, greater<>());
        if (it == m.end()) m.push_back(a); else *it = a;
    }

    cout << m.size() << endl;

    return 0;
}


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i, n) for(int i = 0; i < (n); i++)
#define rep1(i, n) for(int i = 1; i <= (n); i++)
#define co(x) cout << (x) << "\n"
#define cosp(x) cout << (x) << " "
#define ce(x) cerr << (x) << "\n"
#define cesp(x) cerr << (x) << " "
#define pb push_back
#define mp make_pair
#define Would
#define you
#define please

const int cm = 1 << 17;
char cn[cm], * ci = cn + cm, ct;
inline char getcha() {
	if (ci - cn == cm) { fread_unlocked(cn, 1, cm, stdin); ci = cn; }
	return *ci++;
}
inline int getint() {
	int A = 0;
	if (ci - cn + 16 > cm) while ((ct = getcha()) >= '0') A = A * 10 + ct - '0';
	else while ((ct = *ci++) >= '0') A = A * 10 + ct - '0';
	return A;
}

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

	int N = getint();
	int A[100000];
	rep(i, N) A[i] = -1;

	int k = N - 1;

	rep(i, N) {
		int a = getint();
		auto itr = lower_bound(A + k, A + N, a) - 1;
		*itr = a;
		if (itr == A + k) k--;
	}

	co(N - k - 1);

	Would you please return 0;
}


#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

int main(){
    int n;
    cin>>n;
    vector<long long> a(n);
    for(int i=0;i<n;++i) cin>>a[i];
    reverse(a.begin(),a.end());
    vector<long long> dp(n,1);

    for(int i=0;i<n;++i){
        for(int j=0;j<i;++j){
            if(a[i]>=a[j]) dp[i]=max(dp[i],dp[j]+1);
        }
    }

    cout<<*max_element(dp.begin(),dp.end())<<endl;
}


#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#define repeat(i, n) for (int i = 0; (i) < (n); ++(i))
#define repeat_from(i, m, n) for (int i = (m); (i) < (n); ++(i))
using namespace std;
template <class T>
void setmax(T &a, T const &b) {
    if (a < b) a = b;
}

template <class T>
void setmin(T &a, T const &b) {
    if (a > b) a = b;
}

template <typename T, typename X>
auto vectors(T a, X x) { return vector<T>(x, a); }

template <typename T, typename X, typename Y, typename... Zs>
auto vectors(T a, X x, Y y, Zs... zs) {
    auto cont = vectors(a, y, zs...);
    return vector<decltype(cont)>(x, cont);
}

// ei1333
struct UnionFind {
    vector<int> data;

    UnionFind(int sz) {
        data.assign(sz, -1);
    }

    bool unite(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) return (false);
        if (data[x] > data[y]) swap(x, y);
        data[x] += data[y];
        data[y] = x;
        return (true);
    }

    int find(int k) {
        if (data[k] < 0) return (k);
        return (data[k] = find(data[k]));
    }

    int size(int k) {
        return (-data[find(k)]);
    }
};

const int INF = 1e9 + 7;
// ACL contest 1 のA問題

int main() {
    int N;
    cin >> N;
    vector<int> A(N);
    repeat(i, N) cin >> A[i];
    UnionFind uf(N);
    stack<int> stk;
    repeat(i, N) {
        int minA = A[i];
        int minID = i;
        while (!stk.empty()) {
            int id = stk.top();
            if (A[id] >= minA) break;
            // A[id] < minA
            uf.unite(id, i);
            minA = A[id];
            minID = id;
            stk.pop();
        }
        stk.push(minID);
    }
    vector<bool> used(N);
    int res = 0;
    repeat(i, N) {
        int par = uf.find(i);
        if (!used[par]) {
            ++res;
            used[par] = true;
        }
    }
    cout << res << endl;
    return 0;
}


#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define rep(i,n) for (int i = 0; i < int(n);i++)

int main() {
	int n;
	cin >> n;
	vector<int> v(n);
	rep(i,n) cin >> v[i];
	int ans = 1;
	int now = v[0];
	for (int i = 1; i < n;i++){
		if (v[i] <= now){
			ans++;
			now = v[i];
			//cout << "now is " << now << endl;
		}
	}
	cout << ans << endl;

	return 0;
}


#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <math.h>
#ifdef __cplusplus
#include <bits/stdc++.h>
#endif

void rsortr_u32(void *base, const int count){
	uint32_t *a=base;
	uint32_t *b=malloc(count*sizeof(uint32_t));
	int c[0x100];
	memset(c, 0, sizeof(c)); for(int i=0; i<count; i++){ c[(a[i]>>(0<<3))&0xFF]++; } for(int i=0xFE; i>=0; i--) c[i]+=c[i+1]; for(int i=count-1; i>=0; i--){ b[--c[(a[i]>>(0<<3))&0xFF]]=a[i]; }{void *t=a; a=b; b=t;}
	memset(c, 0, sizeof(c)); for(int i=0; i<count; i++){ c[(a[i]>>(1<<3))&0xFF]++; } for(int i=0xFE; i>=0; i--) c[i]+=c[i+1]; for(int i=count-1; i>=0; i--){ b[--c[(a[i]>>(1<<3))&0xFF]]=a[i]; }{void *t=a; a=b; b=t;}
	memset(c, 0, sizeof(c)); for(int i=0; i<count; i++){ c[(a[i]>>(2<<3))&0xFF]++; } for(int i=0xFE; i>=0; i--) c[i]+=c[i+1]; for(int i=count-1; i>=0; i--){ b[--c[(a[i]>>(2<<3))&0xFF]]=a[i]; }{void *t=a; a=b; b=t;}
	memset(c, 0, sizeof(c)); for(int i=0; i<count; i++){ c[(a[i]>>(3<<3))&0xFF]++; } for(int i=0xFE; i>=0; i--) c[i]+=c[i+1]; for(int i=count-1; i>=0; i--){ b[--c[(a[i]>>(3<<3))&0xFF]]=a[i]; }{void *t=a; a=b; b=t;}
	free(b);
}
int32_t nextint(void){ char c=getchar_unlocked(); while(c!='-'&&(c<'0'||'9'<c)) c=getchar_unlocked(); int s=0; if(c=='-'){s=1;c=getchar_unlocked();} uint32_t x=0; while('0'<=c && c<='9'){ x=x*10+c-'0'; c=getchar_unlocked(); } return s?-x:x; }

int d[100000];

int bs(int k, int l, int r){
	while(l<=r){
		int m=(l+r)/2;
		if(d[m]<k) r=m-1; else l=m+1;
	}
	return l;
}

int main(void){
	int n=nextint();
	for(int i=0; i<n; i++){
		d[i]=-1;
		int a=nextint();
		int j=bs(a,0,i-1);
		d[j]=a;
	}
	int ans=bs(0,0,n-1);
	printf("%d\n", ans);
}


#include <stdio.h>

int main(void) {

  long n;
  scanf("%ld", &n);
  long a[n];
  for (long i = 0; i < n; i++) {
    scanf("%ld", &a[i]);
  }
  long list[n];
  list[0] = a[0];
  long list_size = 1;
  long start,end,center;
  for (long i = 1; i < n; i++) {
    if (a[i] <= list[list_size-1]) {
      list[list_size] = a[i];
      list_size++;
    } else {
      if (a[i] > list[0]) {
        list[0] = a[i];
        continue;
      }
      start = 0;
      end = list_size-1;
      while (start+1 != end) {
        center = (start+end)/2;
        if (list[center] >= a[i]) {
          start = center;
        } else {
          end = center;
        }
      }
      list[end] = a[i];
    }
  }
  printf("%ld\n", list_size);

  return 0;
}


#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<inttypes.h>

typedef int32_t i32;

#define ALLOC(size,type) ((type*)calloc((size),sizeof(type)))

void run (void) {
  i32 n;
  scanf ("%" SCNi32, &n);
  i32 *dp = ALLOC (n + 1, i32);
  dp[0] = 1000000000 + 1;
  i32 len = 0;
  for (i32 i = 0; i < n; ++i) {
    i32 a;
    scanf ("%" SCNi32, &a);
    if (dp[len] >= a) {
      dp[++len] = a;
      continue;
    }
    i32 l = 0;
    i32 r = len;
    while (r - l > 1) {
      i32 m = (l + r) / 2;
      if (dp[m] >= a) {
        l = m;
      } else {
        r = m;
      }
    }
    dp[r] = a;
  }
  printf ("%" PRIi32 "\n", len);
}

int main (void) {
  run();
  return 0;
}


#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int num[100009]={0},a[100009]={0},f=0;
int main()
{
    int n,i,mi;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        int n1;
        scanf("%d",&n1);
        num[i]=n1;
    }
    a[1]=num[1];
    f++;
    mi=a[1];
    for(i=2;i<=n;i++){
        if(num[i]<=mi){
            mi=num[i];
            a[++f]=num[i];
        }
        else{
                for(int j=1;j<=f;j++){
                    if(a[j]<num[i]){
                        a[j]=num[i];
                        if(j==f)
                            mi=num[i];
                            break;
                    }
                }
        }
        //printf("%d  %d\n",mi,f);

    }
    printf("%d\n",f);

    return 0;
}


#include <stdio.h>
#include<stdlib.h>
int min(int a, int b){return a < b ? a : b;}
int compare_int(const void *a, const void *b)
{
    return *(int*)a - *(int*)b;
}

int main(void){
    int n;
    int list[100000], a[100000];
    int b[100000];//既に配列を使ったかどうかを記録する

    int i, j, k;
    scanf("%d", &n);
    for(i = 0; i < n; i++){
        scanf("%d", &a[i]);
    }

    b[0] = a[n-1];
    int cnt = 1;
    int c_list[100000];
    int b_min, b_min_i;
    int mid;
    for(i = n - 2; i >= 0; i--){
        int tmp = 0;//塗る事の出来る色をカウント
        //a[i]を何かの色で塗りたい
        //bはソートされているため，b[j] > a[i]を満たす最小のb[j]をとるjの値を二分探索で求める
        //ない場合はflag=0
        if(b[cnt - 1] <= a[i]){
            b[cnt] = a[i];
            cnt++;
        }else if(b[0] > a[i]){
            j = 0;
            b[j] = a[i];
        }else{
            int r = cnt;
            int l = 0;
            while(r - l != 1){
                mid = (r + l) / 2;
                if(b[mid] < a[i]){
                    l = mid;
                }else if(b[mid] > a[i]){
                    r = mid;
                }else{
                    l = mid;
                }
            }
            j = r;
            b[j] = a[i];
        }

        qsort(b, cnt, sizeof(int), compare_int);
    }

    printf("%d\n", cnt);

    return 0;
}


#include <stdio.h>
#include<stdlib.h>
int min(int a, int b){return a < b ? a : b;}
int compare_int(const void *a, const void *b)
{
    return *(int*)a - *(int*)b;
}

int main(void){
    int n;
    int list[100000], a[100000];
    int b[100000];//既に配列を使ったかどうかを記録する

    int i, j, k;
    scanf("%d", &n);
    for(i = 0; i < n; i++){
        scanf("%d", &a[i]);
    }

    b[0] = a[n-1];
    int cnt = 1;
    int c_list[100000];
    int b_min, b_min_i;
    for(i = n - 2; i >= 0; i--){
        int tmp = 0;//塗る事の出来る色をカウント
        //a[i]を何かの色で塗りたい
        for(j = 0; j < cnt; j++){
            if(b[j] > a[i]){
                tmp++;
                if(tmp == 1){
                    b_min = b[i];
                    b_min_i = i;
                }else{
                    if(b_min > b[i]){
                        b_min = b[i];
                        b_min_i = i;
                    }
                }
            }
        }

        //今ある色で塗れない場合
        if(tmp == 0){
            b[cnt] = a[i];
            cnt++;
        }else{//塗れる場合
            b[b_min_i] = a[i];
        }
        //qsort(b, cnt, sizeof(int), compare_int);
    }

    printf("%d\n", cnt);

    return 0;
}


var GET=(function(){function f(s){return new g(s);}function g(s){this._s=s.trim().split("\n");this._y=0;}g.prototype.a=function(f){var s=this._s, y=this._y, r;if(typeof s[y]==="string")s[y]=s[y].split(" ").reverse();r=s[y].pop();if(!s[y].length)this._y++;return f?r:+r;};g.prototype.l=function(f){var s=this._s[this._y++].split(" ");return f?s:s.map(a=>+a);};g.prototype.m=function(n,f){var r=this._s.slice(this._y,this._y+=n).map(a=>a.split(" "));return f?r:r.map(a=>a.map(a=>+a));};g.prototype.r=function(n,f){var r=this._s.slice(this._y,this._y+=n);return f?r:r.map(a=>+a);};return f;})();
var o=GET(require("fs").readFileSync("/dev/stdin","utf8"));
function xArray(v){var a=arguments,l=a.length,r="Array(a["+--l+"]).fill(0).map(x=>{return "+v+";})";while(--l)r="Array(a["+l+"]).fill(0).map(x=>"+r+")";return eval(r);}
function upper_bound(d,a,b,e){for(var c;1<b-a;)d[c=0|(a+b)/2]<=e?a=c:b=c;return d[a]<=e?b:a};

console.log(main());
function main(){
  var n = o.a();
  var a = o.r(n);
  var ans = [1];
  for(var i = 0; i < n; i++){
    var t = -a[i];
    var l = upper_bound(ans,0,ans.length,t);
    ans[l] = t;
  }
  return ans.length;
}


//↓見なくていいよ！ここから--------------------------------------------------
var read = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
var obj;
var inLine = [];
read.on('line', function(input){inLine.push(input);});
read.on('close', function(){
  obj = init(inLine);
  console.error("\n");
  var start = Date.now();
  Main();
  var end = Date.now() - start;
  myerr("time : " + (end) + "ms");
  myerr("memory : " + Math.round(process.memoryUsage().heapTotal / 1024) + "KB");
});
function makeClone(obj){return JSON.parse(JSON.stringify(obj));}
function nextInt(){return myconv(next(),1);} function nextStrArray(){return myconv(next(),2);}
function nextIntArray(){return myconv(next(),4);} function nextCharArray(){return myconv(next(),6);}
function next(){return obj.next();} function hasNext(){return obj.hasNext();}
function init(input){
  var returnObj = {
    list : input, index : 0, max : input.length,
    hasNext : function(){return (this.index < this.max);},
    next : function(){if(!this.hasNext()){throw "ArrayIndexOutOfBoundsException これ以上ないよ";}else{var returnInput = this.list[this.index];this.index++;return returnInput;}}
  };
  return returnObj;
}
function myout(s){console.log(s);}
function myerr(s){console.error("debug:" + require("util").inspect(s,false,null));}
//[no]要素の扱い。数値型
//不明値、異常時:引数そのまま返す 1:数値へ変換
//2:半角SPで分割 4:半角SPで分割し、数値配列へ
//6:1文字で分割  7:1文字で分割し、数値配列へ
//8:半角SPで結合 9:改行で結合 0:文字なしで結合
function myconv(i,no){try{switch(no){case 1:return parseInt(i);case 2:return i.split(" ");case 4:return i.split(" ").map(Number);case 6:return i.split("");case 7:return i.split("").map(Number);case 8:return i.join(" ");case 9:return i.join("\n");case 0:return i.join("");default:return i;}}catch(e){return i;}}

//↑見なくていいよ！ここまで--------------------------------------------------
function Main(){
	var N = nextInt();
	var list = [];
	for(var i = 0; i < N; i++){
		var a = nextInt();
		if(i == 0){
			list.push(a);
			continue;
		}
		var index = binarySearch(list,a);
		if(index == list.length){
			list.push(a);
		}else{
			list[index] = a
		}
		//myerr(list);
	}
	myout(list.length);
}
function binarySearch(list,search){
  //listはあらかじめ昇順ソート済みとする。右側が有効エリア、左側が無効エリアと仮定し、
  //searchによって返す要素(インデックス)は ok:有効な部分の最小値、ng:無効な部分の最大値
  var ng = -1;
  var ok = list.length;
  var center;
  while(Math.abs(ok - ng) > 1){
    center = Math.floor((ok + ng) / 2);
    if(list[center] < search){
       ok = center;
    }else{
       ng = center;
    }
  }
  return ok;
  //return ng;
}


var GET=(function(){function f(s){return new g(s);}function g(s){this._s=s.trim().split("\n");this._y=0;}g.prototype.a=function(f){var s=this._s, y=this._y, r;if(typeof s[y]==="string")s[y]=s[y].split(" ").reverse();r=s[y].pop();if(!s[y].length)this._y++;return f?r:+r;};g.prototype.l=function(f){var s=this._s[this._y++].split(" ");return f?s:s.map(a=>+a);};g.prototype.m=function(n,f){var r=this._s.slice(this._y,this._y+=n).map(a=>a.split(" "));return f?r:r.map(a=>a.map(a=>+a));};g.prototype.r=function(n,f){var r=this._s.slice(this._y,this._y+=n);return f?r:r.map(a=>+a);};return f;})();
var o=GET(require("fs").readFileSync("/dev/stdin","utf8"));
function xArray(v){var a=arguments,l=a.length,r="Array(a["+--l+"]).fill(0).map(x=>{return "+v+";})";while(--l)r="Array(a["+l+"]).fill(0).map(x=>"+r+")";return eval(r);}
var SET=(function(){
  var t,u,ts;
  function f(){return new mySet();}
  function mySet(){
    t=this.tree=[{Parent:-1,isBlack:1}];
    this.root=0;
    u=this.unused=[];
    this.size=0;
    ts=this;
  }
  mySet.prototype.xor=function(x){
    var n=ts.root;
    while(t[n].leftChild!==undefined){
      if(t[n].value===x)return this.deleteAddr(n);
      if(t[n].value<x)n=t[n].rightChild;
      else n=t[n].leftChild;
    }
    add(x,n);
  }
  mySet.prototype.add=function(x){
    var n = ts.root;
    while(t[n].leftChild!==undefined){
      if(t[n].value===x)return false;
      if(t[n].value<x)n=t[n].rightChild;
      else n=t[n].leftChild;
    }
    add(x,n);
    return true;
  }
  function add(x,n){
    var m=n,p=t[m].Parent;
    while(p!==-1){
      if(t[p].leftChild===m)t[p].lChildSize++;
      m=p,p=t[p].Parent;
    }
    treeAdd(n,x);
  }
  function treeAdd(n,x){
    ts.size++;
    t[n].value=x;
    t[n].lChildSize=t[n].isBlack=0;
    createChild(n);
    var p=t[n].Parent;
    if(p===-1){
      //根の処理
      t[n].isBlack=1;
    }else if(t[p].isBlack===0){
      //親が赤の場合の処理
      rotate(n,p);
    }
  }
  function createChild(n){
    var l,r;
    if(u.length){
      l=u.pop();
      t[l].Parent=n;
      if(u.length){
        r=u.pop();
        t[r].Parent=n;
      }else{
        r=t.length;
        t.push({Parent:n,isBlack:1});
      }
    }else{
      l=t.length, r=l+1;
      t.push({Parent:n,isBlack:1},{Parent:n,isBlack:1});
    }
    t[n].leftChild=l;
    t[n].rightChild=r;
  }
  function rotate(n,p){
    var pp=t[p].Parent,p3=t[pp].Parent;
    if(t[n].value<t[p].value){
      if(t[p].value<t[pp].value){
        t[p].Parent=p3;
        t[pp].Parent=p;
        t[pp].leftChild=t[p].rightChild;
        t[p].rightChild=t[t[pp].leftChild].Parent=pp;
        t[pp].lChildSize-=t[p].lChildSize+1;
        t[n].isBlack=1,t[p].isBlack=0;
        if(p3===-1){t[p].isBlack=1;ts.root=p;}else{
          if(t[p].value<t[p3].value)t[p3].leftChild=p;else t[p3].rightChild=p;
          if(t[p3].isBlack===0)rotate(p,p3);
        }
      }else{
        t[pp].rightChild=t[p].Parent=n;
        t[p].leftChild=t[n].rightChild;
        t[p].lChildSize-=t[n].lChildSize+1;
        t[n].rightChild=t[t[p].leftChild].Parent=p;
        t[n].Parent=pp;
        rotate(p,n);
      }
    }else{
      if(t[p].value<t[pp].value){
        t[pp].leftChild=t[p].Parent=n;
        t[p].rightChild=t[n].leftChild;
        t[n].lChildSize+=t[p].lChildSize+1;
        t[n].leftChild=t[t[p].rightChild].Parent=p;
        t[n].Parent=pp;
        rotate(p,n);
      }else{
        t[p].Parent=p3;
        t[pp].Parent=p;
        t[pp].rightChild=t[p].leftChild;
        t[p].leftChild=t[t[pp].rightChild].Parent=pp;
        t[p].lChildSize+=t[pp].lChildSize+1;
        t[n].isBlack=1,t[p].isBlack=0;
        if(p3===-1){t[p].isBlack=1;ts.root=p;}else{
          if(t[p].value<t[p3].value)t[p3].leftChild=p;else t[p3].rightChild=p;
          if(t[p3].isBlack===0)rotate(p,p3);
        }
      }
    }
  }
  mySet.prototype.has=function(x){
    var n=ts.root;
    while(t[n].leftChild!==undefined){
      if(t[n].value===x)return n;
      if(t[n].value<x)n=t[n].rightChild;
      else n=t[n].leftChild;
    }
    return -1;
  }
  mySet.prototype.delete=function(x){
    var n=this.has(x);
    if(n===-1)return false;
    this.deleteAddr(n);
    return true;
  }
  mySet.prototype.deleteAddr=function(n){
    //削除の処理
    var l=t[n].leftChild,r=t[n].rightChild,p=t[n].Parent;
    ts.size--;
    lChildSizeUpdate(n);
    if(t[l].leftChild===undefined){
      if(t[r].isBlack===0){
        t[n].value=t[r].value;
        t[n].rightChild=t[r].rightChild;
        t[t[r].rightChild].Parent=n;
        Unused(r,t[r].leftChild);
        return;
      }
      //子が無い
      var b=t[n].isBlack;
      t[n]={Parent:p,isBlack:1};
      Unused(l,r);
      if(b&&p!==-1){
        if(t[p].leftChild===n)leftShortage(p);
        else rightShortage(p);
      }
      return;
    }
    //交換ノード探索
    var c=l;
    t[n].lChildSize--;
    while(t[t[c].rightChild].leftChild!==undefined)c=t[c].rightChild;
    var b=t[c].isBlack,cl=t[c].leftChild,cp=t[c].Parent,bl=t[cl].isBlack;
    t[n].value=t[c].value;
    if(cp===n)t[n].leftChild=cl;
    else t[cp].rightChild=cl;
    t[cl].Parent=cp;
    Unused(c,t[c].rightChild);
    //交換ノード赤
    if(b===0)return;
    //交換ノード黒左子ノード赤
    if(bl===0){t[cl].isBlack=1;return;}
    //交換ノード黒左子ノード黒
    if(cp===n)leftShortage(n);
    else rightShortage(cp);
  }
  function Unused(a,b){
    u.push(a,b);
    t[a]={isBlack:1},t[b]={isBlack:1};
  }
  function lChildSizeUpdate(n){
    var p=t[n].Parent;
    while(p!==-1){
      if(t[p].leftChild===n)t[p].lChildSize--;
      n=p,p=t[n].Parent;
    }
  }
  function leftShortage(n){
    var r=t[n].rightChild,rl=t[r].leftChild,rr=t[r].rightChild;
    if(t[r].isBlack===0){
      t[r].isBlack=1,t[n].isBlack=0;
      changeChild(n,r);
      t[rl].Parent=t[r].leftChild=n;
      t[n].Parent=r,t[n].rightChild=rl;
      t[r].lChildSize+=t[n].lChildSize+1;
      leftShortage(n);
    }else if(t[rr].isBlack===0){
      t[r].isBlack=t[n].isBlack;
      t[n].isBlack=t[rr].isBlack=1;
      changeChild(n,r);
      t[rl].Parent=t[r].leftChild=n;
      t[n].Parent=r,t[n].rightChild=rl;
      t[r].lChildSize+=t[n].lChildSize+1;
    }else if(t[rl].isBlack===0){
      var rll=t[rl].leftChild,rlr=t[rl].rightChild;
      t[rl].isBlack=t[n].isBlack;
      t[n].isBlack=1;
      changeChild(n,rl);
      t[rll].Parent=t[rl].leftChild=n;
      t[n].rightChild=rll,t[n].Parent=rl;
      t[rlr].Parent=t[rl].rightChild=r;
      t[r].Parent=rl,t[r].leftChild=rlr;
      t[r].lChildSize-=t[rl].lChildSize+1;
      t[rl].lChildSize+=t[n].lChildSize+1;
    }else{
      var b=t[n].isBlack,p=t[n].Parent;
      t[n].isBlack=1,t[r].isBlack=0;
      if(b&&p!==-1){
        if(t[p].leftChild===n)leftShortage(p);
        else rightShortage(p);
      }
    }
  }
  function rightShortage(n){
    var l=t[n].leftChild,ll=t[l].leftChild,lr=t[l].rightChild;
    if(t[l].isBlack===0){
      t[l].isBlack=1,t[n].isBlack=0;
      changeChild(n,l);
      t[lr].Parent=t[l].rightChild=n;
      t[n].Parent=l,t[n].leftChild=lr;
      t[n].lChildSize-=t[l].lChildSize+1;
      rightShortage(n);
    }else if(t[ll].isBlack===0){
      t[l].isBlack=t[n].isBlack;
      t[n].isBlack=t[ll].isBlack=1;
      changeChild(n,l);
      t[lr].Parent=t[l].rightChild=n;
      t[n].Parent=l,t[n].leftChild=lr;
      t[n].lChildSize-=t[l].lChildSize+1;
    }else if(t[lr].isBlack===0){
      var lrl=t[lr].leftChild,lrr=t[lr].rightChild;
      t[lr].isBlack=t[n].isBlack;
      t[n].isBlack=1;
      changeChild(n,lr);
      t[lrr].Parent=t[lr].rightChild=n;
      t[n].leftChild=lrr,t[n].Parent=lr;
      t[lrl].Parent=t[lr].leftChild=l;
      t[l].Parent=lr,t[l].rightChild=lrl;
      t[lr].lChildSize+=t[l].lChildSize+1;
      t[n].lChildSize-=t[lr].lChildSize+1;
    }else{
      var b=t[n].isBlack,p=t[n].Parent;
      t[n].isBlack=1,t[l].isBlack=0;
      if(b&&p!==-1){
        if(t[p].leftChild===n)leftShortage(p);
        else rightShortage(p);
      }
    }
  }
  function changeChild(b,a){
    var p=t[b].Parent;
    t[a].Parent=p;
    if(p===-1){t[a].isBlack=1;ts.root=a;return;}
    if(t[p].leftChild===b)t[p].leftChild=a;
    else t[p].rightChild=a;
  }
  mySet.prototype.get=function(i){
    if(ts.size<=i)return;
    return t[ts.getAddr(i)].value;
  }
  mySet.prototype.getAddr=function(i){
    var r=ts.root,c=t[r].lChildSize;
    while(c!==i){
      if(c<i){
        i-=1+c;
        r=t[r].rightChild;
      }else{
        r=t[r].leftChild;
      }
      c=t[r].lChildSize;
    };
    return r;
  }
  mySet.prototype.lower_bound=function(v){
    var r=ts.root,s=0;
    while(t[r].leftChild!==undefined){
      if(t[r].value===v)return s+t[r].lChildSize;
      if(v<t[r].value){
        r=t[r].leftChild;
      }else{
        s+=t[r].lChildSize+1;
        r=t[r].rightChild;
      }
    }
    return s;
  }
  mySet.prototype.upper_bound=function(v){
    var r=ts.root,s=0;
    while(t[r].leftChild!==undefined){
      if(v<t[r].value){
        r=t[r].leftChild;
      }else{
        s+=t[r].lChildSize+1;
        r=t[r].rightChild;
      }
    }
    return s;
  }
  mySet.prototype.Array=function(){
    var ans=[];
    dfs(this.root);
    return ans;
    function dfs(n){
      if(t[n].leftChild===undefined)return;
      dfs(t[n].leftChild);
      ans.push(t[n].value);
      dfs(t[n].rightChild);
    }
  }
  return f;
})();

console.log(main());
function main(){
  var n = o.a();
  var a = o.r(n);
  var set = SET();
  set.add(-1);
  var map = new Map();
  map.set(-1,1);
  for(var q = 0; q < n; q++){
    var t = set.lower_bound(a[q]);
    if(t){
      var u = set.get(t-1);
      var x = map.get(u);
      map.set(u,x-1);
      if(x === 1)set.delete(u);
    }
    set.add(a[q]);
    var y = map.get(a[q]);
    if(y)map.set(a[q],y+1);
    else map.set(a[q],1);
  }
  var ans = 0;
  for(var k of map){
    ans += k[1];
  }
  return ans;
}


function Main(input) {
  input=input.split("\n");
  if(input[input.length-1]===''){
    input.pop();
  }
  const N=input[0]-0;
  var colors=[];
  var temp;
  var min,max,mid;
  for(var i=1;i<input.length;i++){
    temp=input[i]-0;
    if(colors.length===0){
      colors.push(temp);
      continue;
    }
    if(temp<=colors[0]){
      colors.unshift(temp);
      continue;
    }
    if(temp>colors[colors.length-1]){
      colors[colors.length-1]=temp;
      continue;
    }
    min=0;
    max=colors.length-1;
    while(max-min>1){
      mid=Math.floor((max+min)/2);
      if(colors[mid]<=temp){
        max=mid;
      }else{
        min=mid;
      }
    }
    colors[min]=temp;
  }
  console.log(colors.length);
}

//*この行以降は編集しないでください（標準入出力から一度に読み込み、Mainを呼び出します）
Main(require("fs").readFileSync("/dev/stdin", "utf8"));


var read = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
var obj;
var inLine = [];
read.on('line', function(input){inLine.push(input);});
read.on('close', function(){
  obj = init(inLine);
  myerr("-----start-----");
  var start = new Date();
  Main();
  var end = new Date() - start;
  myerr("----- end -----");
  myerr("time : " + (end) + "ms");
});
function nextInt(){return myconv(next(),1);} function nextStrArray(){return myconv(next(),2);}
function nextIntArray(){return myconv(next(),4);} function nextCharArray(){return myconv(next(),6);}
function next(){return obj.next();} function hasNext(){return obj.hasNext();}
function init(input){
  var returnObj = {
    list : input, index : 0, max : input.length,
    hasNext : function(){return (this.index < this.max);},
    next : function(){if(!this.hasNext()){throw "ArrayIndexOutOfBoundsException これ以上ないよ";}else{var returnInput = this.list[this.index];this.index++;return returnInput;}}
  };
  return returnObj;
}
function myout(s){console.log(s);}
function myerr(s){console.error("debug:" + require("util").inspect(s,false,null));}
//[no]要素の扱い。数値型
//不明値、異常時:引数そのまま返す 1:数値へ変換
//2:半角SPで分割 4:半角SPで分割し、数値配列へ
//6:1文字で分割  7:1文字で分割し、数値配列へ
//8:半角SPで結合 9:改行で結合 0:文字なしで結合
function myconv(i,no){try{switch(no){case 1:return parseInt(i);case 2:return i.split(" ");case 4:return i.split(" ").map(Number);case 6:return i.split("");case 7:return i.split("").map(Number);case 8:return i.join(" ");case 9:return i.join("\n");case 0:return i.join("");default:return i;}}catch(e){return i;}}
function Main(){
  var N = nextInt();
  var list = new Array(N);
  var max = 0;
  var count = 1;
  for(var i = 0; i < N; i++){
    list[i] = nextInt();
  }
  for(var i = N - 1; i > 0; i--){
    if(list[i - 1] >= list[i]){
      if(max <= list[i - 1]){
        max = list[i - 1];
        count++;
      }
    }
  }
  myout(count);
}


var read = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
});
var obj;
var inLine = [];
read.on('line', function(input){inLine.push(input);});
read.on('close', function(){
  obj = init(inLine);
  myerr("-----start-----");
  var start = new Date();
  Main();
  var end = new Date() - start;
  myerr("----- end -----");
  myerr("time : " + (end) + "ms");
});
function nextInt(){return myconv(next(),1);} function nextStrArray(){return myconv(next(),2);}
function nextIntArray(){return myconv(next(),4);} function nextCharArray(){return myconv(next(),6);}
function next(){return obj.next();} function hasNext(){return obj.hasNext();}
function init(input){
  var returnObj = {
    list : input, index : 0, max : input.length,
    hasNext : function(){return (this.index < this.max);},
    next : function(){if(!this.hasNext()){throw "ArrayIndexOutOfBoundsException これ以上ないよ";}else{var returnInput = this.list[this.index];this.index++;return returnInput;}}
  };
  return returnObj;
}
function myout(s){console.log(s);}
function myerr(s){console.error("debug:" + require("util").inspect(s,false,null));}
//[no]要素の扱い。数値型
//不明値、異常時:引数そのまま返す 1:数値へ変換
//2:半角SPで分割 4:半角SPで分割し、数値配列へ
//6:1文字で分割  7:1文字で分割し、数値配列へ
//8:半角SPで結合 9:改行で結合 0:文字なしで結合
function myconv(i,no){try{switch(no){case 1:return parseInt(i);case 2:return i.split(" ");case 4:return i.split(" ").map(Number);case 6:return i.split("");case 7:return i.split("").map(Number);case 8:return i.join(" ");case 9:return i.join("\n");case 0:return i.join("");default:return i;}}catch(e){return i;}}
function Main(){
  var N = nextInt();
  var list = new Array(N);
  var mins = [0];
  for(var i = 0; i < N; i++){
    list[i] = nextInt();
  }
  for(var i = 0; i < N; i++){
    var isOK = false;
    for(var j = 0; j < mins.length; j++){
      if(list[i] > mins[j]){
        mins[j] = list[i];
        isOK = true;
        break;
      }
    }
    if(!isOK){
      mins.push(list[i]);
    }
  }
  myout(mins.length);
}


package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func main() {
	sc := bufio.NewScanner(os.Stdin)
	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())

	arr := make([]int, n)
	for i := 0; i < n; i++ {
		sc.Scan()
		arr[i], _ = strconv.Atoi(sc.Text())
	}

	result := make([]int, 0)
	for _, el := range arr {
		if len(result) == 0 || result[len(result)-1] >= el {
			result = append(result, el)
		} else {
			left := 0
			right := len(result)
			for left < right {
				current := (left + right) / 2
				if result[current] < el {
					right = current
				} else {
					left = current + 1
				}
			}

			result[left] = el
		}
	}

	fmt.Println(len(result))
}


package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

const (
	maxBufferSize     = 2000000 // check
	initialBufferSize = 20000
)

func Reverse(a []int) {
	n := len(a)
	for i := 0; i < n/2; i++ {
		a[i], a[n-i-1] = a[n-i-1], a[i]
	}
}

func BinarySearch(a []int, begin, end, target int) (int, int) {
	lb, ub := begin-1, end
	for ub-lb > 1 {
		mid := (lb + ub) / 2
		if a[mid] <= target {
			lb = mid
		} else {
			ub = mid
		}
	}
	return lb, ub
}

func Fill(a []int, value int) {
	for i := 0; i < len(a); i++ {
		a[i] = value
	}
}

func solve() {
	sc := bufio.NewScanner(os.Stdin)
	buffer := make([]byte, initialBufferSize)
	sc.Buffer(buffer, maxBufferSize)

	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	a := make([]int, n)
	for i := 0; i < n; i++ {
		sc.Scan()
		a[i], _ = strconv.Atoi(sc.Text())
	}

	Reverse(a)

	b := make([]int, n)
	Fill(b, 1010101010)
	b[0] = a[0]
	for i := 1; i < n; i++ {
		_, ub := BinarySearch(b, 0, n, a[i])
		b[ub] = a[i]
	}

	var ans int
	for i := 0; i < n; i++ {
		if b[i] != 1010101010 {
			ans = i + 1
		}
	}

	fmt.Println(ans)
}

func main() {
	solve()
}


package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

const (
	maxBufferSize     = 2000000 // check
	initialBufferSize = 20000
)

func Reverse(a []int) {
	n := len(a)
	for i := 0; i < n/2; i++ {
		a[i], a[n-i-1] = a[n-i-1], a[i]
	}
}

func BinarySearch(a []int, begin, end, target int) (int, int) {
	lb, ub := begin-1, end-1
	for ub-lb > 1 {
		mid := (lb + ub) / 2
		if a[mid] <= target {
			lb = mid
		} else {
			ub = mid
		}
	}
	return lb, ub
}

func Fill(a []int, value int) {
	for i := 0; i < len(a); i++ {
		a[i] = value
	}
}

func solve() {
	sc := bufio.NewScanner(os.Stdin)
	buffer := make([]byte, initialBufferSize)
	sc.Buffer(buffer, maxBufferSize)

	sc.Scan()
	n, _ := strconv.Atoi(sc.Text())
	a := make([]int, n)
	for i := 0; i < n; i++ {
		sc.Scan()
		a[i], _ = strconv.Atoi(sc.Text())
	}

	Reverse(a)

	b := make([]int, n)
	Fill(b, 1010101010)
	b[0] = a[0]
	for i := 1; i < n; i++ {
		_, ub := BinarySearch(b, 0, n, a[i])
		b[ub] = a[i]
	}

	var ans int
	for i := 0; i < n; i++ {
		if b[i] != 1010101010 {
			ans = i + 1
		}
	}

	fmt.Println(ans)
}

func main() {
	solve()
}


package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
)

var sc = bufio.NewScanner(os.Stdin)

func scanInt() int {
	sc.Scan()
	i, err := strconv.Atoi(sc.Text())
	if err != nil {
		panic(err)
	}
	return i
}

func main() {
	sc.Split(bufio.ScanWords)

	N := scanInt()
	A := make([]int, N)
	for i := 0; i < N; i++ {
		A[i] = scanInt()
	}

	minList := make([]int, 1)
	minList[0] = A[0]
	for i := 1; i < N; i++ {
		useNewColor := true
		for j := len(minList) - 1; j >= 0; j-- {
			if A[i] > minList[j] {
				minList[j] = A[i]
				sort.Ints(minList)
				useNewColor = false
				break
			}
		}

		if useNewColor {
			minList = append([]int{A[i]}, minList...)
		}
	}
	fmt.Println(len(minList))
}


package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
)

var sc = bufio.NewScanner(os.Stdin)

func scanInt() int {
	sc.Scan()
	i, err := strconv.Atoi(sc.Text())
	if err != nil {
		panic(err)
	}
	return i
}

func main() {
	sc.Split(bufio.ScanWords)

	N := scanInt()
	A := make([]int, N)
	for i := 0; i < N; i++ {
		A[i] = scanInt()
	}

	minList := make([]int, 1)
	minList[0] = A[0]
	for i := 1; i < N; i++ {
		useNewColor := true
		for j := len(minList) - 1; j >= 0; j-- {
			if A[i] > minList[j] {
				minList[j] = A[i]
				sort.Ints(minList)
				useNewColor = false
				break
			}
		}

		if useNewColor {
			minList = append(minList, A[i])
			sort.Ints(minList)
		}
	}
	fmt.Println(len(minList))
}


package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
)

var sc = bufio.NewScanner(os.Stdin)

func Scanner() string {
	sc.Scan()
	return sc.Text()
}

func intScanner() int {
	n, _ := strconv.Atoi(Scanner())
	return n
}

func floatScanner() float64 {
	n, _ := strconv.ParseFloat(Scanner(), 64)
	return n
}
func stringToInt(s string) int {
	n, _ := strconv.Atoi(s)
	return n
}

var mod int

func main() {
	mod = 1000000007
	buf := make([]byte, 0)
	sc.Buffer(buf, mod)
	sc.Split(bufio.ScanWords)
	n := intScanner()
	as := make([]int, n)
	for i := 0; i < n; i++ {
		as[i] = intScanner()
	}
	xs := []int{as[0]}
	for i := 1; i < n; i++ {
		idx := sort.SearchInts(xs, as[i])
		if idx == 0 {
			xs = append([]int{as[i]}, xs...)
		} else {
			xs = append(xs[:idx-1], xs[idx:]...)
			x := sort.SearchInts(xs, as[i])
			xs = append(append(xs[:x], as[i]), xs[x:]...)
		}
	}
	fmt.Println(len(xs))
}


n = gets.to_s.to_i

a = []
$<.each do |x|
  x = x.to_i
  a[ a.bsearch_index{|k|k<x} || a.size ] = x
end

puts a.size


gets

a = []
$<.each do |x|
  x = x.to_i
  a[ a.bsearch_index{|k|k<x} || a.size ] = x
end

puts a.size


_,*a=gets
$<.map{|x|x=x.to_i
a[a.bsearch_index{|y|y<x}||a.size]=x}
p a.size


b = []
gets.to_i.times.map{ gets.to_i }.each do |a|
  i = b.bsearch_index {|e| e < a }
  if i; b[i] = a; else; b.push a; end
  p b
end
puts b.size


b = []
gets.to_i.times.map{ gets.to_i }.each do |a|
  i = b.bsearch_index {|e| e < a }
  if i; b[i] = a; else; b.unshift a; end
end
puts b.size


line = readlines.map(&:to_i)

N = line[0]

ans = []
for i in 1..N do
  if ans == [] then
    ans.push(line[i])
  else
    found = false
    j = (ans.length - 1) / 2
    min = 0
    max = ans.length
    while found == false do
      if ans[j] >= line[i] then
        min = j + 1
        j = (ans.length + j) / 2
      elsif ans[j] < line[i] then  #can be colored j
        max = j
        j = j / 2
      end
      if min == max then
        found = true
      end
    end
    if min == -1 then
      ans.push(line[i])
    else
      ans[min] = line[i]
    end
  end
end

print ans.length


use std::io::*;
use utils::*;

pub fn main() {
    let i = stdin();
    let mut o = Vec::new();
    run(i.lock(), &mut o);
    stdout().write_all(&o).unwrap();
}

pub fn run<R: BufRead, W: Write>(i: R, o: &mut W) {
    let mut i = CpReader::new(i);
    let n = i.read::<usize>();
    let a = i.read_vec::<usize>(n);
    writeln!(o, "{}", solve(a)).unwrap();
}
fn solve(a: Vec<usize>) -> usize {
    let mut m = Vec::new();
    for a in a {
        let i = binary_search(m.len(), |i| m[i] < a);
        if i == m.len() {
            m.push(a);
        } else {
            m[i] = a;
        }
    }
    m.len()
}

pub mod utils {
    use super::*;

    pub struct CpReader<R: BufRead> {
        r: R,
        b: Vec<u8>,
    }
    impl<R: BufRead> CpReader<R> {
        pub fn new(r: R) -> Self {
            CpReader {
                r: r,
                b: Vec::new(),
            }
        }
        pub fn read_word(&mut self) -> &[u8] {
            self.b.clear();
            let mut consume = 0;
            loop {
                self.r.consume(consume);
                let b = self.r.fill_buf().unwrap();
                assert!(b.len() != 0);
                if let Some(p) = b.iter().position(|&x| x == b' ' || x == b'\n') {
                    self.b.extend_from_slice(&b[..p]);
                    consume = p + 1;
                    break;
                }
                self.b.extend_from_slice(b);
                consume = b.len();
            }
            self.r.consume(consume);
            &self.b
        }
        pub fn read_word_str(&mut self) -> &str {
            unsafe { std::str::from_utf8_unchecked(self.read_word()) }
        }

        pub fn read_line(&mut self) -> &[u8] {
            self.b.clear();
            self.r.read_until(b'\n', &mut self.b).unwrap();
            if self.b.last() == Some(&b'\n') {
                &self.b[..self.b.len() - 1]
            } else {
                &self.b
            }
        }
        pub fn read_line_str(&mut self) -> &str {
            unsafe { std::str::from_utf8_unchecked(self.read_line()) }
        }

        pub fn read<T: CpIn>(&mut self) -> T {
            T::read_from(self)
        }

        pub fn read_vec<T: CpIn>(&mut self, n: usize) -> Vec<T> {
            (0..n).map(|_| self.read()).collect()
        }
        pub fn read_iter<'a, T: CpIn>(&'a mut self, n: usize) -> CpIter<'a, R, T> {
            CpIter {
                r: self,
                n: n,
                _pd: Default::default(),
            }
        }
    }
    pub struct CpIter<'a, R: BufRead + 'a, T> {
        r: &'a mut CpReader<R>,
        n: usize,
        _pd: std::marker::PhantomData<fn() -> T>,
    }
    impl<'a, R: BufRead, T: CpIn> Iterator for CpIter<'a, R, T> {
        type Item = T;
        fn next(&mut self) -> Option<T> {
            if self.n == 0 {
                None
            } else {
                self.n -= 1;
                Some(self.r.read())
            }
        }
    }

    pub trait CpIn {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self;
    }

    impl CpIn for u64 {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
            read_u64_fast(&mut r.r)
        }
    }
    impl CpIn for i64 {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
            read_i64_fast(&mut r.r)
        }
    }
    impl CpIn for char {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
            let b = r.r.fill_buf().unwrap()[0] as char;
            r.r.consume(1);
            let s = r.r.fill_buf().unwrap()[0];
            assert!(s == b' ' || s == b'\n');
            r.r.consume(1);
            b
        }
    }

    macro_rules! cpin_tuple {
        ($($t:ident),*) => {
            impl<$($t: CpIn),*> CpIn for ($($t),*) {
                fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
                    ($($t::read_from(r)),*)
                }
            }
        };
    }

    macro_rules! cpin_cast {
        ($t_self:ty, $t_read:ty) => {
            impl CpIn for $t_self {
                fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
                    <$t_read>::read_from(r) as $t_self
                }
            }
        };
    }
    macro_rules! cpin_parse {
        ($t:ty) => {
            impl CpIn for $t {
                fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
                    r.read_word_str().parse().unwrap()
                }
            }
        };
    }
    cpin_cast!(usize, u64);
    cpin_cast!(u32, u64);
    cpin_cast!(u16, u64);
    cpin_cast!(i32, i64);
    cpin_cast!(i16, i64);
    cpin_cast!(i8, i64);

    cpin_parse!(f64);

    cpin_tuple!(T1, T2);
    cpin_tuple!(T1, T2, T3);
    cpin_tuple!(T1, T2, T3, T4);
    cpin_tuple!(T1, T2, T3, T4, T5);

    fn read_u64_fast<R: BufRead>(r: &mut R) -> u64 {
        let mut value = 0;
        loop {
            let mut idx = 0;
            let buf_len;
            {
                let buf = r.fill_buf().unwrap();
                buf_len = buf.len();
                assert!(buf_len != 0);
                while idx < buf_len && b'0' <= buf[idx] && buf[idx] <= b'9' {
                    value = value * 10 + (buf[idx] - b'0') as u64;
                    idx += 1;
                }
            }
            if idx < buf_len {
                r.consume(idx + 1);
                return value;
            }
            r.consume(idx);
        }
    }
    fn read_i64_fast<R: BufRead>(r: &mut R) -> i64 {
        let (consume, sign) = match r.fill_buf().unwrap()[0] {
            b'+' => (true, 1),
            b'-' => (true, -1),
            _ => (false, 1),
        };
        if consume {
            r.consume(1);
        }
        read_u64_fast(r) as i64 * sign
    }

    pub fn binary_search<F: Fn(usize) -> bool>(len: usize, f: F) -> usize {
        let mut low = 0;
        let mut high = len;
        while low < high {
            let mid = (low + high) / 2;
            if f(mid) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        low
    }

}


use std::io::*;
use utils::*;

pub fn main() {
    let i = stdin();
    let mut o = Vec::new();
    run(i.lock(), &mut o);
    stdout().write_all(&o).unwrap();
}

pub fn run<R: BufRead, W: Write>(i: R, o: &mut W) {
    let mut i = CpReader::new(i);
    let n = i.read::<usize>();
    let a = i.read_iter::<usize>(n);

    let mut m = Vec::new();
    for a in a {
        let i = binary_search(m.len(), |i| m[i] < a);
        if i == m.len() {
            m.push(a);
        } else {
            m[i] = a;
        }
    }
    writeln!(o, "{}", m.len()).unwrap();
}

pub mod utils {
    use super::*;

    pub struct CpReader<R: BufRead> {
        r: R,
        b: Vec<u8>,
    }
    impl<R: BufRead> CpReader<R> {
        pub fn new(r: R) -> Self {
            CpReader {
                r: r,
                b: Vec::new(),
            }
        }
        pub fn read_word(&mut self) -> &[u8] {
            self.b.clear();
            let mut consume = 0;
            loop {
                self.r.consume(consume);
                let b = self.r.fill_buf().unwrap();
                assert!(b.len() != 0);
                if let Some(p) = b.iter().position(|&x| x == b' ' || x == b'\n') {
                    self.b.extend_from_slice(&b[..p]);
                    consume = p + 1;
                    break;
                }
                self.b.extend_from_slice(b);
                consume = b.len();
            }
            self.r.consume(consume);
            &self.b
        }
        pub fn read_word_str(&mut self) -> &str {
            unsafe { std::str::from_utf8_unchecked(self.read_word()) }
        }

        pub fn read_line(&mut self) -> &[u8] {
            self.b.clear();
            self.r.read_until(b'\n', &mut self.b).unwrap();
            if self.b.last() == Some(&b'\n') {
                &self.b[..self.b.len() - 1]
            } else {
                &self.b
            }
        }
        pub fn read_line_str(&mut self) -> &str {
            unsafe { std::str::from_utf8_unchecked(self.read_line()) }
        }

        pub fn read<T: CpIn>(&mut self) -> T {
            T::read_from(self)
        }

        pub fn read_vec<T: CpIn>(&mut self, n: usize) -> Vec<T> {
            (0..n).map(|_| self.read()).collect()
        }
        pub fn read_iter<'a, T: CpIn>(&'a mut self, n: usize) -> CpIter<'a, R, T> {
            CpIter {
                r: self,
                n: n,
                _pd: Default::default(),
            }
        }
    }
    pub struct CpIter<'a, R: BufRead + 'a, T> {
        r: &'a mut CpReader<R>,
        n: usize,
        _pd: std::marker::PhantomData<fn() -> T>,
    }
    impl<'a, R: BufRead, T: CpIn> Iterator for CpIter<'a, R, T> {
        type Item = T;
        fn next(&mut self) -> Option<T> {
            if self.n == 0 {
                None
            } else {
                self.n -= 1;
                Some(self.r.read())
            }
        }
    }

    pub trait CpIn {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self;
    }

    impl CpIn for u64 {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
            read_u64_fast(&mut r.r)
        }
    }
    impl CpIn for i64 {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
            read_i64_fast(&mut r.r)
        }
    }
    impl CpIn for char {
        fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
            let b = r.r.fill_buf().unwrap()[0] as char;
            r.r.consume(1);
            let s = r.r.fill_buf().unwrap()[0];
            assert!(s == b' ' || s == b'\n');
            r.r.consume(1);
            b
        }
    }

    macro_rules! cpin_tuple {
        ($($t:ident),*) => {
            impl<$($t: CpIn),*> CpIn for ($($t),*) {
                fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
                    ($($t::read_from(r)),*)
                }
            }
        };
    }

    macro_rules! cpin_cast {
        ($t_self:ty, $t_read:ty) => {
            impl CpIn for $t_self {
                fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
                    <$t_read>::read_from(r) as $t_self
                }
            }
        };
    }
    macro_rules! cpin_parse {
        ($t:ty) => {
            impl CpIn for $t {
                fn read_from<R: BufRead>(r: &mut CpReader<R>) -> Self {
                    r.read_word_str().parse().unwrap()
                }
            }
        };
    }
    cpin_cast!(usize, u64);
    cpin_cast!(u32, u64);
    cpin_cast!(u16, u64);
    cpin_cast!(i32, i64);
    cpin_cast!(i16, i64);
    cpin_cast!(i8, i64);

    cpin_parse!(f64);

    cpin_tuple!(T1, T2);
    cpin_tuple!(T1, T2, T3);
    cpin_tuple!(T1, T2, T3, T4);
    cpin_tuple!(T1, T2, T3, T4, T5);

    fn read_u64_fast<R: BufRead>(r: &mut R) -> u64 {
        let mut value = 0;
        loop {
            let mut idx = 0;
            let buf_len;
            {
                let buf = r.fill_buf().unwrap();
                buf_len = buf.len();
                assert!(buf_len != 0);
                while idx < buf_len && b'0' <= buf[idx] && buf[idx] <= b'9' {
                    value = value * 10 + (buf[idx] - b'0') as u64;
                    idx += 1;
                }
            }
            if idx < buf_len {
                r.consume(idx + 1);
                return value;
            }
            r.consume(idx);
        }
    }
    fn read_i64_fast<R: BufRead>(r: &mut R) -> i64 {
        let (consume, sign) = match r.fill_buf().unwrap()[0] {
            b'+' => (true, 1),
            b'-' => (true, -1),
            _ => (false, 1),
        };
        if consume {
            r.consume(1);
        }
        read_u64_fast(r) as i64 * sign
    }

    pub fn binary_search<F: Fn(usize) -> bool>(len: usize, f: F) -> usize {
        let mut low = 0;
        let mut high = len;
        while low < high {
            let mid = (low + high) / 2;
            if f(mid) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        low
    }

}


fn read_stdin() -> String {
    use std::io::Read;
    let mut input = String::new();
    std::io::stdin().read_to_string(&mut input).unwrap();
    input
}

fn scan<'a, I: Iterator<Item = &'a str>, T: std::str::FromStr>(tokens: &mut I) -> T {
    tokens.next().unwrap().parse().ok().unwrap()
}

fn main() {
    let input = read_stdin();
    let tokens = &mut input.split_whitespace();
    let n: usize = scan(tokens);
    let mut x: Vec<usize> = Vec::new();
    for _ in 0..n {
        let a: usize = scan(tokens);
        if x.is_empty() || x[x.len() - 1] >= a {
            x.push(a);
        } else if x[0] < a {
            x[0] = a;
        } else {
            let mut i = 0;
            let mut j = x.len() - 1;
            while i + 1 < j {
                let c = (i + j) / 2;
                if x[c] >= a {
                    i = c;
                } else {
                    j = c;
                }
            }
            x[j] = a;
        }
    }
    println!("{}", x.len());
}


use proconio::input;
use superslice::*;

fn main() {
    input! {
        n: usize,
        a: [usize; n],
    }

    let mut v = vec![];

    v.push(a[0]);

    for &na in a.iter().skip(1) {
        if na <= v[0] {
            v.push(na);
            continue;
        } else {
            let index = v.upper_bound(&(na - 1));
            v[index - 1] = na;
        }
    }

    println!("{}", v.len());
}


use std::cmp;
use std::collections::BTreeSet;
use std::collections::VecDeque;
use std::fmt::Debug;
use std::str::FromStr;
#[allow(dead_code)]
fn read_as_vec<T>() -> Vec<T>
where
    T: FromStr,
    <T as FromStr>::Err: Debug,
{
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    s.trim()
        .split_whitespace()
        .map(|c| T::from_str(c).unwrap())
        .collect()
}

#[allow(dead_code)]
fn read_as_string() -> String {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    s.trim().to_string()
}

fn lower_bound<T: Ord + Debug>(a: &VecDeque<T>, t: T) -> Option<usize> {
    if a.len() == 0 || t <= a[0] {
        return None;
    }
    let (mut left, mut right) = (0, a.len());
    while left + 1 < right {
        let mid = (left + right) / 2;
        //println!("left={}, right={}, mid={}", left, right, mid);
        if t > a[mid] {
            left = mid;
        } else {
            right = mid;
        }
    }
    return Some(left);
}

fn main() {
    let n: usize = read_as_vec()[0];
    let mut a: VecDeque<usize> = VecDeque::new();

    for _ in 0..n {
        let t = read_as_vec::<usize>()[0];
        if let Some(pos) = lower_bound(&a, t) {
            a[pos] = t;
        } else {
            a.push_back(t);
        }
    }
    println!("{}", a.len());

    // let mut b: VecDeque<usize> = VecDeque::new();
    // b.push_back(3);
    // b.push_back(4);
    // b.push_back(4);
    // b.push_back(4);
    // b.push_back(5);
    // b.push_back(5);
    // b.push_back(6);
    // b.push_back(6);
    // dbg!(lower_bound(&b, 5));
}


use std::cmp;
use std::collections::BTreeSet;
use std::collections::VecDeque;
use std::fmt::Debug;
use std::str::FromStr;
#[allow(dead_code)]
fn read_as_vec<T>() -> Vec<T>
where
    T: FromStr,
    <T as FromStr>::Err: Debug,
{
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    s.trim()
        .split_whitespace()
        .map(|c| T::from_str(c).unwrap())
        .collect()
}

#[allow(dead_code)]
fn read_as_string() -> String {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    s.trim().to_string()
}

fn lower_bound<T: Ord + Debug>(a: &VecDeque<T>, t: T) -> Option<usize> {
    if a.len() == 0 || t <= a[0] {
        return None;
    }
    let (mut left, mut right) = (0, a.len());
    while left + 1 < right {
        let mid = (left + right) / 2;
        if t > a[mid] {
            left = mid;
        } else {
            right = mid - 1;
        }
    }
    return Some(left);
}

fn main() {
    let n: usize = read_as_vec()[0];
    let mut a: VecDeque<usize> = VecDeque::new();

    for _ in 0..n {
        let t = read_as_vec::<usize>()[0];
        if let Some(pos) = lower_bound(&a, t) {
            a[pos] = t;
        } else {
            a.push_back(t);
        }
    }
    println!("{}", a.len());

    // let mut b: VecDeque<usize> = VecDeque::new();
    // b.push_back(3);
    // b.push_back(4);
    // b.push_back(4);
    // b.push_back(4);
    // b.push_back(5);
    // b.push_back(5);
    // b.push_back(6);
    // b.push_back(6);
    // dbg!(lower_bound(&b, 5));
}


let n = Int(readLine()!)!
var a: [Int] = .init(repeating: 0, count: n)
for i in 0..<n {
  a[i] = Int(readLine()!)!
}
var ans: [Int] = []

func binarySearch(_ a: [Int], _ target: Int) -> Int {
  var left = 0
  var right = ans.count - 1
  var ans = a.count
  while left <= right {
    let mid = left + (right - left) / 2
    if target > a[mid] {
      ans = mid
      right = mid - 1
    } else {
      left = mid + 1
    }
  }
  return max(ans, 0)
}

for i in 0..<n {
  let ai = a[i]
  let index = binarySearch(ans, ai)
  if index >= ans.count {
    ans.append(ai)
  } else {
    ans[index] = ai
  }
}
print(ans.count)


let n = Int(readLine()!)!
var a: [Int] = .init(repeating: 0, count: n)
for i in 0..<n {
  a[i] = Int(readLine()!)!
}
var ans: [Int] = []

func binarySearch(_ a: [Int], _ target: Int) -> Int {
  var left = 0
  var right = ans.count - 1
  var ans = a.count
  while left <= right {
    let mid = left + (right - left) / 2
    if target > a[mid] {
      ans = mid
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  return max(ans, 0)
}

for i in 0..<n {
  let ai = a[i]
  let index = binarySearch(ans, ai)
  if index >= ans.count {
    ans.append(ai)
  } else {
    ans[index] = ai
  }
}
print(ans.count)


let n = Int(readLine()!)!
var a: [Int] = .init(repeating: 0, count: n)
for i in 0..<n {
  a[i] = Int(readLine()!)!
}
var ans: [Int] = []

func binarySearch(_ a: [Int], _ target: Int) -> Int {
  var left = 0
  var right = ans.count - 1
  var ans = a.count
  while left <= right {
    let mid = left + (right - left) / 2
    if target > a[mid] {
      ans = mid
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  return max(ans, 0)
}

for i in 0..<n {
  let ai = -a[i]
  let index = binarySearch(ans, ai)
  if index >= ans.count {
    ans.append(ai)
  } else {
    ans[index] = ai
  }
}
print(ans.count)


let n = Int(readLine()!)!
var a: [Int] = .init(repeating: 0, count: n)
for i in 0..<n {
  a[i] = Int(readLine()!)!
}
var ans: [Int] = []

func binarySearch(_ a: [Int], _ target: Int) -> Int {
  var left = 0
  var right = ans.count - 1
  var ans = a.count
  while left <= right {
    let mid = left + (right - left) / 2
    if target > a[mid] {
      ans = mid
      left = mid + 1
    } else {
      right = mid - 1
    }
  }
  return max(ans, 0)
}

for i in 0..<n {
  let ai = -a[i]
  let index = binarySearch(ans, ai)
  if index >= ans.count {
    ans.append(ai)
  } else {
    ans[index] = ai
  }
}
print(ans)
print(ans.count)


import * as fs from 'fs';

export class Reader {
    lines: string[];
    currentLine: number;

    constructor() {
        this.lines = fs.readFileSync("/dev/stdin", "utf8").trim().split("\n");
        this.currentLine = 0;
    }

    readLine(): string {
        return this.lines[this.currentLine++];
    }

    readInt(): number {
        return Number(this.readLine());
    }

    readInts(): number[] {
        return this.readLine().split(" ").map(Number);
    }
}

let reader = new Reader();

let N = reader.readInt();
let A: number[] = [];
for(let i=0; i<N; i++) {
    A.push(reader.readInt());
}

let B = new Array(2*N).fill(-1);
let start = N, end = N;

for(let a of A) {
    if (start == end && B[start] == -1) {
        B[start] = a;
        continue
    }
    if (a <= B[start]) {
        B[--start] = a;
        continue
    }
    if (a > B[end]) {
        B[end] = a;
        continue
    }
    let low = start-1, high = end+1;
    while (high-low > 1) {
        let mid = Math.floor((low+high)/2);
        if (a > B[mid]) {
            low = mid;
        } else {
            high = mid;
        }
    }
    if (B[low] == -1)
        B[--start] = a;
    else
        B[low] = a;
}

let count = B.reduce((prev, current) => prev+((current >= 0)?1:0), 0);
console.log(count);


function main(input) {
  input = input.trim().split("\n");
  const n = Number(input[0]);
  const as = input.slice(1).map(n => Number(n));
  const colors = [];
  for (let i = 0; i < n; i++) {
    const a = as[i];
    const target = (() => {
      if (!colors.length || a <= colors[colors.length-1]) return null;
      for (let j = 0; j < colors.length; j++) {
        const num = colors[j];
        if (num < a) return j;
      }
      return null;
    })();
    if (target === null) {
      colors.push(a);
    } else {
      colors[target] = a;
    }
  }
  const result = colors.length;
  console.log(result);
}
main(require("fs").readFileSync("/dev/stdin", "utf8"));


function main(input) {
  input = input.trim().split("\n");
  const n = Number(input[0]);
  const as = input.slice(1).map(n => Number(n));
  const colors = [];
  for (let i = 0; i < n; i++) {
    const a = as[i];
    const target = (() => {
      if (!colors.length || a < colors[colors.length-1]) return null;
      for (let j = 0; j < colors.length; j++) {
        const num = colors[j];
        if (num < a) return j;
      }
      return null;
    })();
    if (target === null) {
      colors.push(a);
    } else {
      colors[target] = a;
    }
  }
  const result = colors.length;
  console.log(result);
}
main(require("fs").readFileSync("/dev/stdin", "utf8"));


x1, y1, x2, y2 = map(int, input().split())

ans = ''

dx = x2 - x1
dy = y2 - y1

ans += 'R'*dx + 'U'*dy + 'R' + 'D'*(dy+1) + 'L'*(dx+1) + 'U'*(dy+1) + 'R'*dx + 'U' + 'L'*(dx+1) + 'D'*(dy+1) + 'R'

print(ans)


# UU  R     DD  L
#LUUU RR DR DDD LLU

sx, sy, tx, ty = map(int, input().split())
print('U'*abs(ty-sy),
        'R'*abs(tx-sx),
        'D'*abs(ty-sy),
        'L'*abs(tx-sx),
        'L',
        'U'*(abs(ty-sy)+1),
        'R'*(abs(tx-sx)+1),
        'DR',
        'D'*(abs(ty-sy)+1),
        'L'*(abs(tx-sx)+1),
        'U',
        sep='')


sx,sy,tx,ty=map(int,input().split())
xd=tx-sx;yd=ty-sy
res='R'*xd+'U'*yd+'L'*xd+'D'*yd+'D'+'R'*(xd+1)+'U'*(yd+1)+'LU'+'L'*(xd+1)+'D'*(yd+1)+'R'
print(res)


def main():
    sx, sy, tx, ty = map(int, input().split())
    x = tx - sx
    y = ty - sy
    s = ""
    for i in range(2):
        tmp = ""
        if i:
            tmp += "D"
            tmp += "R"*(x+1)
            tmp += "U"*(y+1)
            tmp += "L"

        else:
            tmp += "R"*x
            s += "U"*y
        s += tmp + tmp.translate(str.maketrans({"U": "D",
                                                "D": "U", "L": "R", "R": "L"}))
    print(s)

if __name__ == "__main__":
    main()


def main():
    sx, sy, tx, ty = map(int, input().split())
    x = tx - sx
    y = ty - sy
    s = ""
    for i in range(2):
        if i:
            s += "D"
            s += "R"*(x+1)
            s += "U"*(y+1)
            s += "L"

        else:
            s += "R"*x
            s += "U"*y
        s += s.translate(str.maketrans({"U": "D",
                                        "D": "U", "L": "R", "R": "L"}))
    print(s)

if __name__ == "__main__":
    main()


def main():
    sx, sy, tx, ty = map(int, input().split())
    x = tx - sx
    y = ty - sy
    s = ""
    for i in range(2):
        if i:
            s += "D"
            s += "R"*x
            s += "U"*y
            s += "L"

        else:
            s += "R"*x
            s += "U"*y

        s += s.translate(str.maketrans({"U": "D",
                                        "D": "U", "L": "R", "R": "L"}))
    print(s)

if __name__ == "__main__":
    main()


import java.io.*;
import java.util.*;
import static java.lang.System.out;

public class Main{
    static MyReader in = new MyReader();

    public static void main(String[] args){
        int sx, sy, tx, ty;
        {int[] a = in.ii(); sx = a[0]; sy = a[1]; tx = a[2]; ty = a[3];}
        StringBuilder sb = new StringBuilder();
        int x = sx;
        int y = sy;
        int dx = tx-sx;
        int dy = ty-sy;
        char[] ans = new char[4*(dx+dy)+8];
        int j = 0;
        for(int i = dx; i != 0; i--){
            ans[j++] = 'R';
        }
        for(int i = dy; i != 0; i--){
            ans[j++] = 'U';
        }
        for(int i = dx; i != 0; i--){
            ans[j++] = 'L';
        }
        for(int i = dy+1; i != 0; i--){
            ans[j++] = 'D';
        }
        for(int i = dx+1; i != 0; i--){
            ans[j++] = 'R';
        }
        for(int i = dy+1; i != 0; i--){
            ans[j++] = 'U';
        }
        ans[j++] = 'L';
        ans[j++] = 'U';
        for(int i = dx+1; i != 0; i--){
            ans[j++] = 'L';
        }
        for(int i = dy+1; i != 0; i--){
            ans[j++] = 'D';
        }
        ans[j++] = 'R';
        out.println(ans);
    }

    static class MyReader extends BufferedReader{
        MyReader(){
            super(new InputStreamReader(System.in));
        }

        String s(){
            try{return readLine();}catch(IOException e){return "";}
        }

        String[] ss(){
            return s().split(" ");
        }

        int i(){
            return Integer.parseInt(s());
        }

        int[] ii(){
            String[] ss = ss();
            int[] ii = new int[ss.length];
            for(int j = 0; j < ss.length; j++) ii[j] = Integer.parseInt(ss[j]);
            return ii;
        }

        ArrayList<Integer> li(){
            String[] ss = ss();
            ArrayList<Integer> li = new ArrayList<>();
            for(int j = 0; j < ss.length; j++) li.add(Integer.parseInt(ss[j]));
            return li;
        }

        long l(){
            return Long.parseLong(s());
        }

        long[] ll(){
            String[] ss = ss();
            long[] ll = new long[ss.length];
            for(int j = 0; j < ss.length; j++) ll[j] = Long.parseLong(ss[j]);
            return ll;
        }
    }
}


import java.io.*;
import java.util.*;

public class Main {
  private static void append(StringBuilder sb, char c, int count) {
    for (int i = 0; i < count; i++) {
      sb.append(c);
    }
  }

  private static String solve(int sx, int sy, int tx, int ty) {
    StringBuilder sb = new StringBuilder();
    // s -> t
    append(sb, 'R', tx - sx);
    append(sb, 'U', ty - sy);
    // t -> s
    append(sb, 'L', tx - sx);
    append(sb, 'D', ty - sy);

    // s -> t
    append(sb, 'D', 1);
    append(sb, 'R', tx - sx + 1);
    append(sb, 'U', ty - sy + 1);
    append(sb, 'L', 1);

    // t -> s
    append(sb, 'U', 1);
    append(sb, 'L', tx - sx + 1);
    append(sb, 'D', ty - sy + 1);
    append(sb, 'R', 1);
    return sb.toString();
  }

  private static void execute(ContestReader reader, PrintWriter out) {
    int sx = reader.nextInt();
    int sy = reader.nextInt();
    int tx = reader.nextInt();
    int ty = reader.nextInt();
    out.println(solve(sx, sy, tx, ty));
  }

  public static void main(String[] args) {
    ContestReader reader = new ContestReader(System.in);
    PrintWriter out = new PrintWriter(System.out);
    execute(reader, out);
    out.flush();
  }
}

class ContestReader {
  private BufferedReader reader;
  private StringTokenizer tokenizer;

  ContestReader(InputStream in) {
    reader = new BufferedReader(new InputStreamReader(in));
  }

  public String next() {
    while (tokenizer == null || !tokenizer.hasMoreTokens()) {
      try {
        tokenizer = new java.util.StringTokenizer(reader.readLine());
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
    return tokenizer.nextToken();
  }

  public int nextInt() {
    return Integer.parseInt(next());
  }

  public long nextLong() {
    return Long.parseLong(next());
  }

  public double nextDouble() {
    return Double.parseDouble(next());
  }

  public String[] nextArray(int n) {
    String[] array = new String[n];
    for (int i = 0; i < n; i++) {
      array[i] = next();
    }
    return array;
  }

  public int[] nextIntArray(int n) {
    int[] array = new int[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextInt();
    }
    return array;
  }

  public long[] nextLongArray(int n) {
    long[] array = new long[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextLong();
    }
    return array;
  }

  public double[] nextDoubleArray(int n) {
    double[] array = new double[n];
    for (int i = 0; i < n; i++) {
      array[i] = nextDouble();
    }
    return array;
  }

  public int[][] nextIntMatrix(int n, int m) {
    int[][] matrix = new int[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextInt();
      }
    }
    return matrix;
  }

  public long[][] nextLongMatrix(int n, int m) {
    long[][] matrix = new long[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextLong();
      }
    }
    return matrix;
  }

  public double[][] nextDoubleMatrix(int n, int m) {
    double[][] matrix = new double[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = nextDouble();
      }
    }
    return matrix;
  }
}

class ModCalculator {
  private final long mod;

  ModCalculator(long mod) {
    this.mod = mod;
  }

  public long add(long a, long b) {
    return (a + b) % mod;
  }

  public long mul(long a, long b) {
    return (a * b) % mod;
  }

  public long pow(long a, long b) {
    if (b == 0) {
      return 1;
    }
    long v = pow(mul(a, a), b / 2);
    if (b % 2 == 1) {
      return mul(v, a);
    } else {
      return v;
    }
  }

  public long inverse(long a) {
    return pow(a, mod - 2);
  }

  public long div(long a, long b) {
    return mul(a, inverse(b));
  }
}

class ModCombinationCache {
  private final ModCalculator modCalculator;
  private final long[] factorialCache;
  private final long[] factorialInverseCache;

  public ModCombinationCache(int n, ModCalculator modCalculator) {
    this.modCalculator = modCalculator;
    factorialCache = new long[n];
    factorialCache[0] = 1;
    for (int i = 1; i < n; i++) {
      factorialCache[i] = modCalculator.mul(factorialCache[i-1], i);
    }

    factorialInverseCache = new long[n];
    for (int i = 0; i < n; i++) {
      factorialInverseCache[i] = modCalculator.inverse(factorialCache[i]);
    }
  }

  public long getFactorial(int n) {
    return factorialCache[n];
  }

  public long getCombination(int n, int k) {
    return modCalculator.mul(factorialCache[n], modCalculator.mul(factorialInverseCache[k], factorialInverseCache[n-k]));
  }
}

class Algorithm {
  private static void swap(Object[] list, int a, int b) {
    Object tmp = list[a];
    list[a] = list[b];
    list[b] = tmp;
  }

  public static <T extends Comparable<? super T>> boolean nextPermutation(T[] ts) {
    int rightMostAscendingOrderIndex = ts.length - 2;
    while (rightMostAscendingOrderIndex >= 0 &&
        ts[rightMostAscendingOrderIndex].compareTo(ts[rightMostAscendingOrderIndex + 1]) >= 0) {
      rightMostAscendingOrderIndex--;
    }
    if (rightMostAscendingOrderIndex < 0) {
      return false;
    }

    int rightMostGreatorIndex = ts.length - 1;
    while (ts[rightMostAscendingOrderIndex].compareTo(ts[rightMostGreatorIndex]) >= 0) {
      rightMostGreatorIndex--;
    }

    swap(ts, rightMostAscendingOrderIndex, rightMostGreatorIndex);
    for (int i = 0; i < (ts.length - rightMostAscendingOrderIndex - 1) / 2; i++) {
      swap(ts, rightMostAscendingOrderIndex + 1 + i, ts.length - 1 - i);
    }
    return true;
  }

  public static void shuffle(int[] array) {
    Random random = new Random();
    int n = array.length;
    for (int i = 0; i < n; i++) {
      int randomIndex = i + random.nextInt(n - i);

      int temp = array[i];
      array[i] = array[randomIndex];
      array[randomIndex] = temp;
    }
  }

  public static void shuffle(long[] array) {
    Random random = new Random();
    int n = array.length;
    for (int i = 0; i < n; i++) {
      int randomIndex = i + random.nextInt(n - i);

      long temp = array[i];
      array[i] = array[randomIndex];
      array[randomIndex] = temp;
    }
  }

  public static void sort(int[] array) {
    shuffle(array);
    Arrays.sort(array);
  }

  public static void sort(long[] array) {
    shuffle(array);
    Arrays.sort(array);
  }
}

class UnionFind {
  int[] parents;
  int[] ranks;

  UnionFind(int n) {
    parents = new int[n];
    ranks = new int[n];

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

  public int getRoot(int index) {
    if (parents[index] == index) {
      return index;
    } else {
      parents[index] = getRoot(parents[index]);
      return parents[index];
    }
  }

  public boolean sameGroup(int a, int b) {
    return getRoot(a) == getRoot(b);
  }

  public void merge(int a, int b) {
    int rootA = getRoot(a);
    int rootB = getRoot(b);
    if (rootA == rootB) {
      return;
    }
    if (ranks[rootA] < ranks[rootB]) {
      parents[rootA] = rootB;
    } else if (ranks[rootB] < ranks[rootA]) {
      parents[rootB] = rootA;
    } else {
      parents[rootA] = rootB;
      ranks[rootB]++;
    }
  }
}


import java.lang.*;
import java.io.*;
import java.util.*;

public class Main {

	public static void main(String[] args) throws java.lang.Exception {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskA solver = new TaskA();
		solver.solve(in, out);
		out.close();
	}
}

class TaskA {

	public void solve(InputReader in, PrintWriter out) {
		int sx = in.nextInt(), sy = in.nextInt();
		int tx = in.nextInt(), ty = in.nextInt();
		StringBuilder sb = new StringBuilder();
		String sud = "UD";
		String srl = "RL";

		for (int j=0; j<2; ++j) {
			char ch1 = sud.charAt(j), ch2 = srl.charAt(j);
			for (int i=sy; i<ty; ++i) {
				sb.append(ch1);
			}
			for (int i=sx; i<tx; ++i) {
				sb.append(ch2);
			}
		}

		for (int j=0; j<2; ++j) {
			char ch1 = sud.charAt(j), ch2 = srl.charAt(j);
			sb.append(srl.charAt(j^1));
			for (int i=sy; i<=ty; ++i) {
				sb.append(ch1);
			}
			for (int i=sx; i<=tx; ++i) {
				sb.append(ch2);
			}
			sb.append(sud.charAt(j^1));
		}

		out.println(sb.toString());
	}
}

class InputReader {
	public BufferedReader reader;
	public StringTokenizer tokenizer;

	public InputReader(InputStream stream) {
		reader = new BufferedReader(new InputStreamReader(stream), 32768);
		tokenizer = null;
	}

	public String next() {
		while (tokenizer==null || !tokenizer.hasMoreTokens()) {
			try {
				tokenizer = new StringTokenizer(reader.readLine());
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return tokenizer.nextToken();
	}

	public int nextInt() {
		return Integer.parseInt(next());
	}

	public long nextLong() {
		return Long.parseLong(next());
	}

	public double nextDouble() {
		return Double.parseDouble(next());
	}
}