import std.algorithm;
import std.array;
import std.container;
import std.conv;
import std.math;
import std.numeric;
import std.range;
import std.stdio;
import std.string;
import std.typecons;

void scan(T...)(ref T a) {
    string[] ss = readln.split;
    foreach (i, t; T) a[i] = ss[i].to!t;
}
T read(T)() { return readln.chomp.to!T; }
T[] reads(T)() { return readln.split.to!(T[]); }
alias readint = read!int;
alias readints = reads!int;

int calc(int[] a) {
    auto dq = new Deque!int(a.length);
    foreach (e; a) {
        int p = dq.lowerBound(e);
        if (p == 0) dq.pushFront(e);
        else dq[p - 1] = e;
    }
    return dq.size();
}

void main() {
    int n = readint;
    int[] a;
    foreach (_; 0..n) a ~= readint;
    writeln(calc(a));
}

class Deque(T) {
    private T[] _buffer;
    private size_t _size;
    private int _front;
    private int _back;

    this(size_t size) {
        _buffer = new T[size * 3];
        _size = 0;
        _front = _back = cast(int)size * 3 / 2;
    }

    @property bool empty() const {
        return _size == 0;
    }

    int size() const {
        return cast(int)_size;
    }

    void pushFront(T elt) {
        _buffer[empty ? _front : --_front] = elt;
        _size++;
    }

    void pushBack(T elt) {
        _buffer[empty ? _back : ++_back] = elt;
        _size++;
    }

    T opIndex(size_t index) const {
        return _buffer[_front + index];
    }

    T opIndexAssign(T value, size_t index) {
        return _buffer[_front + index] = value;
    }

    int lowerBound(T value) const {
        int lo = 0;
        int hi = cast(int)size() - 1;
        int minIndex = cast(int)size();
        while (lo <= hi) {
            int m = (lo + hi) / 2;
            if (this[m] >= value) {
                minIndex = min(minIndex, m);
                hi = m - 1;
            }
            else lo = m + 1;
        }
        return minIndex;
    }
}
