You are given a sequence with N integers: A = \{ A_1, A_2, \cdots, A_N \}.
For each of these N integers, we will choose a color and paint the integer with that color. Here the following condition must be satisfied:
 - If A_i and A_j (i < j) are painted with the same color, A_i < A_j.
Find the minimum number of colors required to satisfy the condition.

-----Constraints-----
 - 1 \leq N \leq 10^5
 - 0 \leq A_i \leq 10^9

-----Input-----
Input is given from Standard Input in the following format:
N
A_1
:
A_N

-----Output-----
Print the minimum number of colors required to satisfy the condition.

-----Sample Input-----
5
2
1
4
5
3

-----Sample Output-----
2

We can satisfy the condition with two colors by, for example, painting 2 and 3 red and painting 1, 4, and 5 blue.

Difficulty: interview

Starter code:

Solutions:
from bisect import bisect_right


def main():
    n = int(input())
    a = [int(input()) for _ in range(n)]
    a.reverse()
    lis = [a[0]]
    for i in range(1, n):
        if a[i] >= lis[-1]:
            lis.append(a[i])
        else:
            lis[bisect_right(lis, a[i])] = a[i]
    print((len(lis)))


def __starting_point():
    main()

__starting_point()

"""Sorted List
==============
:doc:`Sorted Containers<index>` is an Apache2 licensed Python sorted
collections library, written in pure-Python, and fast as C-extensions. The
:doc:`introduction<introduction>` is the best way to get started.
Sorted list implementations:
.. currentmodule:: sortedcontainers
* :class:`SortedList`
* :class:`SortedKeyList`
"""
# pylint: disable=too-many-lines


import sys
import traceback
from bisect import bisect_left, bisect_right, insort
from itertools import chain, repeat, starmap
from math import log
from operator import add, eq, ne, gt, ge, lt, le, iadd
from textwrap import dedent
from collections.abc import Sequence, MutableSequence, ItemsView, KeysView, ValuesView, MutableSet, Set
from functools import wraps, reduce
from _thread import get_ident


def recursive_repr(fillvalue='...'):
    "Decorator to make a repr function return fillvalue for a recursive call."
    # pylint: disable=missing-docstring
    # Copied from reprlib in Python 3
    # https://hg.python.org/cpython/file/3.6/Lib/reprlib.py

    def decorating_function(user_function):
        repr_running = set()

        @wraps(user_function)
        def wrapper(self):
            key = id(self), get_ident()
            if key in repr_running:
                return fillvalue
            repr_running.add(key)
            try:
                result = user_function(self)
            finally:
                repr_running.discard(key)
            return result

        return wrapper

    return decorating_function

###############################################################################
# END Python 2/3 Shims
###############################################################################


class SortedList(MutableSequence):
    """Sorted list is a sorted mutable sequence.
    Sorted list values are maintained in sorted order.
    Sorted list values must be comparable. The total ordering of values must
    not change while they are stored in the sorted list.
    Methods for adding values:
    * :func:`SortedList.add`
    * :func:`SortedList.update`
    * :func:`SortedList.__add__`
    * :func:`SortedList.__iadd__`
    * :func:`SortedList.__mul__`
    * :func:`SortedList.__imul__`
    Methods for removing values:
    * :func:`SortedList.clear`
    * :func:`SortedList.discard`
    * :func:`SortedList.remove`
    * :func:`SortedList.pop`
    * :func:`SortedList.__delitem__`
    Methods for looking up values:
    * :func:`SortedList.bisect_left`
    * :func:`SortedList.bisect_right`
    * :func:`SortedList.count`
    * :func:`SortedList.index`
    * :func:`SortedList.__contains__`
    * :func:`SortedList.__getitem__`
    Methods for iterating values:
    * :func:`SortedList.irange`
    * :func:`SortedList.islice`
    * :func:`SortedList.__iter__`
    * :func:`SortedList.__reversed__`
    Methods for miscellany:
    * :func:`SortedList.copy`
    * :func:`SortedList.__len__`
    * :func:`SortedList.__repr__`
    * :func:`SortedList._check`
    * :func:`SortedList._reset`
    Sorted lists use lexicographical ordering semantics when compared to other
    sequences.
    Some methods of mutable sequences are not supported and will raise
    not-implemented error.
    """
    DEFAULT_LOAD_FACTOR = 1000

    def __init__(self, iterable=None, key=None):
        """Initialize sorted list instance.
        Optional `iterable` argument provides an initial iterable of values to
        initialize the sorted list.
        Runtime complexity: `O(n*log(n))`
        >>> sl = SortedList()
        >>> sl
        SortedList([])
        >>> sl = SortedList([3, 1, 2, 5, 4])
        >>> sl
        SortedList([1, 2, 3, 4, 5])
        :param iterable: initial values (optional)
        """
        assert key is None
        self._len = 0
        self._load = self.DEFAULT_LOAD_FACTOR
        self._lists = []
        self._maxes = []
        self._index = []
        self._offset = 0

        if iterable is not None:
            self._update(iterable)

    def __new__(cls, iterable=None, key=None):
        """Create new sorted list or sorted-key list instance.
        Optional `key`-function argument will return an instance of subtype
        :class:`SortedKeyList`.
        >>> sl = SortedList()
        >>> isinstance(sl, SortedList)
        True
        >>> sl = SortedList(key=lambda x: -x)
        >>> isinstance(sl, SortedList)
        True
        >>> isinstance(sl, SortedKeyList)
        True
        :param iterable: initial values (optional)
        :param key: function used to extract comparison key (optional)
        :return: sorted list or sorted-key list instance
        """
        # pylint: disable=unused-argument
        if key is None:
            return object.__new__(cls)
        else:
            if cls is SortedList:
                return object.__new__(SortedKeyList)
            else:
                raise TypeError('inherit SortedKeyList for key argument')

    @property
    def key(self):  # pylint: disable=useless-return
        """Function used to extract comparison key from values.
        Sorted list compares values directly so the key function is none.
        """
        return None

    def _reset(self, load):
        """Reset sorted list load factor.
        The `load` specifies the load-factor of the list. The default load
        factor of 1000 works well for lists from tens to tens-of-millions of
        values. Good practice is to use a value that is the cube root of the
        list size. With billions of elements, the best load factor depends on
        your usage. It's best to leave the load factor at the default until you
        start benchmarking.
        See :doc:`implementation` and :doc:`performance-scale` for more
        information.
        Runtime complexity: `O(n)`
        :param int load: load-factor for sorted list sublists
        """
        values = reduce(iadd, self._lists, [])
        self._clear()
        self._load = load
        self._update(values)

    def clear(self):
        """Remove all values from sorted list.
        Runtime complexity: `O(n)`
        """
        self._len = 0
        del self._lists[:]
        del self._maxes[:]
        del self._index[:]
        self._offset = 0

    _clear = clear

    def add(self, value):
        """Add `value` to sorted list.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList()
        >>> sl.add(3)
        >>> sl.add(1)
        >>> sl.add(2)
        >>> sl
        SortedList([1, 2, 3])
        :param value: value to add to sorted list
        """
        _lists = self._lists
        _maxes = self._maxes

        if _maxes:
            pos = bisect_right(_maxes, value)

            if pos == len(_maxes):
                pos -= 1
                _lists[pos].append(value)
                _maxes[pos] = value
            else:
                insort(_lists[pos], value)

            self._expand(pos)
        else:
            _lists.append([value])
            _maxes.append(value)

        self._len += 1

    def _expand(self, pos):
        """Split sublists with length greater than double the load-factor.
        Updates the index when the sublist length is less than double the load
        level. This requires incrementing the nodes in a traversal from the
        leaf node to the root. For an example traversal see
        ``SortedList._loc``.
        """
        _load = self._load
        _lists = self._lists
        _index = self._index

        if len(_lists[pos]) > (_load << 1):
            _maxes = self._maxes

            _lists_pos = _lists[pos]
            half = _lists_pos[_load:]
            del _lists_pos[_load:]
            _maxes[pos] = _lists_pos[-1]

            _lists.insert(pos + 1, half)
            _maxes.insert(pos + 1, half[-1])

            del _index[:]
        else:
            if _index:
                child = self._offset + pos
                while child:
                    _index[child] += 1
                    child = (child - 1) >> 1
                _index[0] += 1

    def update(self, iterable):
        """Update sorted list by adding all values from `iterable`.
        Runtime complexity: `O(k*log(n))` -- approximate.
        >>> sl = SortedList()
        >>> sl.update([3, 1, 2])
        >>> sl
        SortedList([1, 2, 3])
        :param iterable: iterable of values to add
        """
        _lists = self._lists
        _maxes = self._maxes
        values = sorted(iterable)

        if _maxes:
            if len(values) * 4 >= self._len:
                values.extend(chain.from_iterable(_lists))
                values.sort()
                self._clear()
            else:
                _add = self.add
                for val in values:
                    _add(val)
                return

        _load = self._load
        _lists.extend(values[pos:(pos + _load)]
                      for pos in range(0, len(values), _load))
        _maxes.extend(sublist[-1] for sublist in _lists)
        self._len = len(values)
        del self._index[:]

    _update = update

    def __contains__(self, value):
        """Return true if `value` is an element of the sorted list.
        ``sl.__contains__(value)`` <==> ``value in sl``
        Runtime complexity: `O(log(n))`
        >>> sl = SortedList([1, 2, 3, 4, 5])
        >>> 3 in sl
        True
        :param value: search for value in sorted list
        :return: true if `value` in sorted list
        """
        _maxes = self._maxes

        if not _maxes:
            return False

        pos = bisect_left(_maxes, value)

        if pos == len(_maxes):
            return False

        _lists = self._lists
        idx = bisect_left(_lists[pos], value)

        return _lists[pos][idx] == value

    def discard(self, value):
        """Remove `value` from sorted list if it is a member.
        If `value` is not a member, do nothing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList([1, 2, 3, 4, 5])
        >>> sl.discard(5)
        >>> sl.discard(0)
        >>> sl == [1, 2, 3, 4]
        True
        :param value: `value` to discard from sorted list
        """
        _maxes = self._maxes

        if not _maxes:
            return

        pos = bisect_left(_maxes, value)

        if pos == len(_maxes):
            return

        _lists = self._lists
        idx = bisect_left(_lists[pos], value)

        if _lists[pos][idx] == value:
            self._delete(pos, idx)

    def remove(self, value):
        """Remove `value` from sorted list; `value` must be a member.
        If `value` is not a member, raise ValueError.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList([1, 2, 3, 4, 5])
        >>> sl.remove(5)
        >>> sl == [1, 2, 3, 4]
        True
        >>> sl.remove(0)
        Traceback (most recent call last):
          ...
        ValueError: 0 not in list
        :param value: `value` to remove from sorted list
        :raises ValueError: if `value` is not in sorted list
        """
        _maxes = self._maxes

        if not _maxes:
            raise ValueError('{0!r} not in list'.format(value))

        pos = bisect_left(_maxes, value)

        if pos == len(_maxes):
            raise ValueError('{0!r} not in list'.format(value))

        _lists = self._lists
        idx = bisect_left(_lists[pos], value)

        if _lists[pos][idx] == value:
            self._delete(pos, idx)
        else:
            raise ValueError('{0!r} not in list'.format(value))

    def _delete(self, pos, idx):
        """Delete value at the given `(pos, idx)`.
        Combines lists that are less than half the load level.
        Updates the index when the sublist length is more than half the load
        level. This requires decrementing the nodes in a traversal from the
        leaf node to the root. For an example traversal see
        ``SortedList._loc``.
        :param int pos: lists index
        :param int idx: sublist index
        """
        _lists = self._lists
        _maxes = self._maxes
        _index = self._index

        _lists_pos = _lists[pos]

        del _lists_pos[idx]
        self._len -= 1

        len_lists_pos = len(_lists_pos)

        if len_lists_pos > (self._load >> 1):
            _maxes[pos] = _lists_pos[-1]

            if _index:
                child = self._offset + pos
                while child > 0:
                    _index[child] -= 1
                    child = (child - 1) >> 1
                _index[0] -= 1
        elif len(_lists) > 1:
            if not pos:
                pos += 1

            prev = pos - 1
            _lists[prev].extend(_lists[pos])
            _maxes[prev] = _lists[prev][-1]

            del _lists[pos]
            del _maxes[pos]
            del _index[:]

            self._expand(prev)
        elif len_lists_pos:
            _maxes[pos] = _lists_pos[-1]
        else:
            del _lists[pos]
            del _maxes[pos]
            del _index[:]

    def _loc(self, pos, idx):
        """Convert an index pair (lists index, sublist index) into a single
        index number that corresponds to the position of the value in the
        sorted list.
        Many queries require the index be built. Details of the index are
        described in ``SortedList._build_index``.
        Indexing requires traversing the tree from a leaf node to the root. The
        parent of each node is easily computable at ``(pos - 1) // 2``.
        Left-child nodes are always at odd indices and right-child nodes are
        always at even indices.
        When traversing up from a right-child node, increment the total by the
        left-child node.
        The final index is the sum from traversal and the index in the sublist.
        For example, using the index from ``SortedList._build_index``::
            _index = 14 5 9 3 2 4 5
            _offset = 3
        Tree::
                 14
              5	  9
            3   2  4   5
        Converting an index pair (2, 3) into a single index involves iterating
        like so:
        1. Starting at the leaf node: offset + alpha = 3 + 2 = 5. We identify
           the node as a left-child node. At such nodes, we simply traverse to
           the parent.
        2. At node 9, position 2, we recognize the node as a right-child node
           and accumulate the left-child in our total. Total is now 5 and we
           traverse to the parent at position 0.
        3. Iteration ends at the root.
        The index is then the sum of the total and sublist index: 5 + 3 = 8.
        :param int pos: lists index
        :param int idx: sublist index
        :return: index in sorted list
        """
        if not pos:
            return idx

        _index = self._index

        if not _index:
            self._build_index()

        total = 0

        # Increment pos to point in the index to len(self._lists[pos]).

        pos += self._offset

        # Iterate until reaching the root of the index tree at pos = 0.

        while pos:

            # Right-child nodes are at odd indices. At such indices
            # account the total below the left child node.

            if not pos & 1:
                total += _index[pos - 1]

            # Advance pos to the parent node.

            pos = (pos - 1) >> 1

        return total + idx

    def _pos(self, idx):
        """Convert an index into an index pair (lists index, sublist index)
        that can be used to access the corresponding lists position.
        Many queries require the index be built. Details of the index are
        described in ``SortedList._build_index``.
        Indexing requires traversing the tree to a leaf node. Each node has two
        children which are easily computable. Given an index, pos, the
        left-child is at ``pos * 2 + 1`` and the right-child is at ``pos * 2 +
        2``.
        When the index is less than the left-child, traversal moves to the
        left sub-tree. Otherwise, the index is decremented by the left-child
        and traversal moves to the right sub-tree.
        At a child node, the indexing pair is computed from the relative
        position of the child node as compared with the offset and the remaining
        index.
        For example, using the index from ``SortedList._build_index``::
            _index = 14 5 9 3 2 4 5
            _offset = 3
        Tree::
                 14
              5	  9
            3   2  4   5
        Indexing position 8 involves iterating like so:
        1. Starting at the root, position 0, 8 is compared with the left-child
           node (5) which it is greater than. When greater the index is
           decremented and the position is updated to the right child node.
        2. At node 9 with index 3, we again compare the index to the left-child
           node with value 4. Because the index is the less than the left-child
           node, we simply traverse to the left.
        3. At node 4 with index 3, we recognize that we are at a leaf node and
           stop iterating.
        4. To compute the sublist index, we subtract the offset from the index
           of the leaf node: 5 - 3 = 2. To compute the index in the sublist, we
           simply use the index remaining from iteration. In this case, 3.
        The final index pair from our example is (2, 3) which corresponds to
        index 8 in the sorted list.
        :param int idx: index in sorted list
        :return: (lists index, sublist index) pair
        """
        if idx < 0:
            last_len = len(self._lists[-1])

            if (-idx) <= last_len:
                return len(self._lists) - 1, last_len + idx

            idx += self._len

            if idx < 0:
                raise IndexError('list index out of range')
        elif idx >= self._len:
            raise IndexError('list index out of range')

        if idx < len(self._lists[0]):
            return 0, idx

        _index = self._index

        if not _index:
            self._build_index()

        pos = 0
        child = 1
        len_index = len(_index)

        while child < len_index:
            index_child = _index[child]

            if idx < index_child:
                pos = child
            else:
                idx -= index_child
                pos = child + 1

            child = (pos << 1) + 1

        return (pos - self._offset, idx)

    def _build_index(self):
        """Build a positional index for indexing the sorted list.
        Indexes are represented as binary trees in a dense array notation
        similar to a binary heap.
        For example, given a lists representation storing integers::
            0: [1, 2, 3]
            1: [4, 5]
            2: [6, 7, 8, 9]
            3: [10, 11, 12, 13, 14]
        The first transformation maps the sub-lists by their length. The
        first row of the index is the length of the sub-lists::
            0: [3, 2, 4, 5]
        Each row after that is the sum of consecutive pairs of the previous
        row::
            1: [5, 9]
            2: [14]
        Finally, the index is built by concatenating these lists together::
            _index = [14, 5, 9, 3, 2, 4, 5]
        An offset storing the start of the first row is also stored::
            _offset = 3
        When built, the index can be used for efficient indexing into the list.
        See the comment and notes on ``SortedList._pos`` for details.
        """
        row0 = list(map(len, self._lists))

        if len(row0) == 1:
            self._index[:] = row0
            self._offset = 0
            return

        head = iter(row0)
        tail = iter(head)
        row1 = list(starmap(add, zip(head, tail)))

        if len(row0) & 1:
            row1.append(row0[-1])

        if len(row1) == 1:
            self._index[:] = row1 + row0
            self._offset = 1
            return

        size = 2 ** (int(log(len(row1) - 1, 2)) + 1)
        row1.extend(repeat(0, size - len(row1)))
        tree = [row0, row1]

        while len(tree[-1]) > 1:
            head = iter(tree[-1])
            tail = iter(head)
            row = list(starmap(add, zip(head, tail)))
            tree.append(row)

        reduce(iadd, reversed(tree), self._index)
        self._offset = size * 2 - 1

    def __delitem__(self, index):
        """Remove value at `index` from sorted list.
        ``sl.__delitem__(index)`` <==> ``del sl[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList('abcde')
        >>> del sl[2]
        >>> sl
        SortedList(['a', 'b', 'd', 'e'])
        >>> del sl[:2]
        >>> sl
        SortedList(['d', 'e'])
        :param index: integer or slice for indexing
        :raises IndexError: if index out of range
        """
        if isinstance(index, slice):
            start, stop, step = index.indices(self._len)

            if step == 1 and start < stop:
                if start == 0 and stop == self._len:
                    return self._clear()
                elif self._len <= 8 * (stop - start):
                    values = self._getitem(slice(None, start))
                    if stop < self._len:
                        values += self._getitem(slice(stop, None))
                    self._clear()
                    return self._update(values)

            indices = range(start, stop, step)

            # Delete items from greatest index to least so
            # that the indices remain valid throughout iteration.

            if step > 0:
                indices = reversed(indices)

            _pos, _delete = self._pos, self._delete

            for index in indices:
                pos, idx = _pos(index)
                _delete(pos, idx)
        else:
            pos, idx = self._pos(index)
            self._delete(pos, idx)

    def __getitem__(self, index):
        """Lookup value at `index` in sorted list.
        ``sl.__getitem__(index)`` <==> ``sl[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList('abcde')
        >>> sl[1]
        'b'
        >>> sl[-1]
        'e'
        >>> sl[2:5]
        ['c', 'd', 'e']
        :param index: integer or slice for indexing
        :return: value or list of values
        :raises IndexError: if index out of range
        """
        _lists = self._lists

        if isinstance(index, slice):
            start, stop, step = index.indices(self._len)

            if step == 1 and start < stop:
                # Whole slice optimization: start to stop slices the whole
                # sorted list.

                if start == 0 and stop == self._len:
                    return reduce(iadd, self._lists, [])

                start_pos, start_idx = self._pos(start)
                start_list = _lists[start_pos]
                stop_idx = start_idx + stop - start

                # Small slice optimization: start index and stop index are
                # within the start list.

                if len(start_list) >= stop_idx:
                    return start_list[start_idx:stop_idx]

                if stop == self._len:
                    stop_pos = len(_lists) - 1
                    stop_idx = len(_lists[stop_pos])
                else:
                    stop_pos, stop_idx = self._pos(stop)

                prefix = _lists[start_pos][start_idx:]
                middle = _lists[(start_pos + 1):stop_pos]
                result = reduce(iadd, middle, prefix)
                result += _lists[stop_pos][:stop_idx]

                return result

            if step == -1 and start > stop:
                result = self._getitem(slice(stop + 1, start + 1))
                result.reverse()
                return result

            # Return a list because a negative step could
            # reverse the order of the items and this could
            # be the desired behavior.

            indices = range(start, stop, step)
            return list(self._getitem(index) for index in indices)
        else:
            if self._len:
                if index == 0:
                    return _lists[0][0]
                elif index == -1:
                    return _lists[-1][-1]
            else:
                raise IndexError('list index out of range')

            if 0 <= index < len(_lists[0]):
                return _lists[0][index]

            len_last = len(_lists[-1])

            if -len_last < index < 0:
                return _lists[-1][len_last + index]

            pos, idx = self._pos(index)
            return _lists[pos][idx]

    _getitem = __getitem__

    def __setitem__(self, index, value):
        """Raise not-implemented error.
        ``sl.__setitem__(index, value)`` <==> ``sl[index] = value``
        :raises NotImplementedError: use ``del sl[index]`` and
            ``sl.add(value)`` instead
        """
        message = 'use ``del sl[index]`` and ``sl.add(value)`` instead'
        raise NotImplementedError(message)

    def __iter__(self):
        """Return an iterator over the sorted list.
        ``sl.__iter__()`` <==> ``iter(sl)``
        Iterating the sorted list while adding or deleting values may raise a
        :exc:`RuntimeError` or fail to iterate over all values.
        """
        return chain.from_iterable(self._lists)

    def __reversed__(self):
        """Return a reverse iterator over the sorted list.
        ``sl.__reversed__()`` <==> ``reversed(sl)``
        Iterating the sorted list while adding or deleting values may raise a
        :exc:`RuntimeError` or fail to iterate over all values.
        """
        return chain.from_iterable(map(reversed, reversed(self._lists)))

    def reverse(self):
        """Raise not-implemented error.
        Sorted list maintains values in ascending sort order. Values may not be
        reversed in-place.
        Use ``reversed(sl)`` for an iterator over values in descending sort
        order.
        Implemented to override `MutableSequence.reverse` which provides an
        erroneous default implementation.
        :raises NotImplementedError: use ``reversed(sl)`` instead
        """
        raise NotImplementedError('use ``reversed(sl)`` instead')

    def islice(self, start=None, stop=None, reverse=False):
        """Return an iterator that slices sorted list from `start` to `stop`.
        The `start` and `stop` index are treated inclusive and exclusive,
        respectively.
        Both `start` and `stop` default to `None` which is automatically
        inclusive of the beginning and end of the sorted list.
        When `reverse` is `True` the values are yielded from the iterator in
        reverse order; `reverse` defaults to `False`.
        >>> sl = SortedList('abcdefghij')
        >>> it = sl.islice(2, 6)
        >>> list(it)
        ['c', 'd', 'e', 'f']
        :param int start: start index (inclusive)
        :param int stop: stop index (exclusive)
        :param bool reverse: yield values in reverse order
        :return: iterator
        """
        _len = self._len

        if not _len:
            return iter(())

        start, stop, _ = slice(start, stop).indices(self._len)

        if start >= stop:
            return iter(())

        _pos = self._pos

        min_pos, min_idx = _pos(start)

        if stop == _len:
            max_pos = len(self._lists) - 1
            max_idx = len(self._lists[-1])
        else:
            max_pos, max_idx = _pos(stop)

        return self._islice(min_pos, min_idx, max_pos, max_idx, reverse)

    def _islice(self, min_pos, min_idx, max_pos, max_idx, reverse):
        """Return an iterator that slices sorted list using two index pairs.
        The index pairs are (min_pos, min_idx) and (max_pos, max_idx), the
        first inclusive and the latter exclusive. See `_pos` for details on how
        an index is converted to an index pair.
        When `reverse` is `True`, values are yielded from the iterator in
        reverse order.
        """
        _lists = self._lists

        if min_pos > max_pos:
            return iter(())

        if min_pos == max_pos:
            if reverse:
                indices = reversed(range(min_idx, max_idx))
                return map(_lists[min_pos].__getitem__, indices)

            indices = range(min_idx, max_idx)
            return map(_lists[min_pos].__getitem__, indices)

        next_pos = min_pos + 1

        if next_pos == max_pos:
            if reverse:
                min_indices = range(min_idx, len(_lists[min_pos]))
                max_indices = range(max_idx)
                return chain(
                    map(_lists[max_pos].__getitem__, reversed(max_indices)),
                    map(_lists[min_pos].__getitem__, reversed(min_indices)),
                )

            min_indices = range(min_idx, len(_lists[min_pos]))
            max_indices = range(max_idx)
            return chain(
                map(_lists[min_pos].__getitem__, min_indices),
                map(_lists[max_pos].__getitem__, max_indices),
            )

        if reverse:
            min_indices = range(min_idx, len(_lists[min_pos]))
            sublist_indices = range(next_pos, max_pos)
            sublists = map(_lists.__getitem__, reversed(sublist_indices))
            max_indices = range(max_idx)
            return chain(
                map(_lists[max_pos].__getitem__, reversed(max_indices)),
                chain.from_iterable(map(reversed, sublists)),
                map(_lists[min_pos].__getitem__, reversed(min_indices)),
            )

        min_indices = range(min_idx, len(_lists[min_pos]))
        sublist_indices = range(next_pos, max_pos)
        sublists = map(_lists.__getitem__, sublist_indices)
        max_indices = range(max_idx)
        return chain(
            map(_lists[min_pos].__getitem__, min_indices),
            chain.from_iterable(sublists),
            map(_lists[max_pos].__getitem__, max_indices),
        )

    def irange(self, minimum=None, maximum=None, inclusive=(True, True),
               reverse=False):
        """Create an iterator of values between `minimum` and `maximum`.
        Both `minimum` and `maximum` default to `None` which is automatically
        inclusive of the beginning and end of the sorted list.
        The argument `inclusive` is a pair of booleans that indicates whether
        the minimum and maximum ought to be included in the range,
        respectively. The default is ``(True, True)`` such that the range is
        inclusive of both minimum and maximum.
        When `reverse` is `True` the values are yielded from the iterator in
        reverse order; `reverse` defaults to `False`.
        >>> sl = SortedList('abcdefghij')
        >>> it = sl.irange('c', 'f')
        >>> list(it)
        ['c', 'd', 'e', 'f']
        :param minimum: minimum value to start iterating
        :param maximum: maximum value to stop iterating
        :param inclusive: pair of booleans
        :param bool reverse: yield values in reverse order
        :return: iterator
        """
        _maxes = self._maxes

        if not _maxes:
            return iter(())

        _lists = self._lists

        # Calculate the minimum (pos, idx) pair. By default this location
        # will be inclusive in our calculation.

        if minimum is None:
            min_pos = 0
            min_idx = 0
        else:
            if inclusive[0]:
                min_pos = bisect_left(_maxes, minimum)

                if min_pos == len(_maxes):
                    return iter(())

                min_idx = bisect_left(_lists[min_pos], minimum)
            else:
                min_pos = bisect_right(_maxes, minimum)

                if min_pos == len(_maxes):
                    return iter(())

                min_idx = bisect_right(_lists[min_pos], minimum)

        # Calculate the maximum (pos, idx) pair. By default this location
        # will be exclusive in our calculation.

        if maximum is None:
            max_pos = len(_maxes) - 1
            max_idx = len(_lists[max_pos])
        else:
            if inclusive[1]:
                max_pos = bisect_right(_maxes, maximum)

                if max_pos == len(_maxes):
                    max_pos -= 1
                    max_idx = len(_lists[max_pos])
                else:
                    max_idx = bisect_right(_lists[max_pos], maximum)
            else:
                max_pos = bisect_left(_maxes, maximum)

                if max_pos == len(_maxes):
                    max_pos -= 1
                    max_idx = len(_lists[max_pos])
                else:
                    max_idx = bisect_left(_lists[max_pos], maximum)

        return self._islice(min_pos, min_idx, max_pos, max_idx, reverse)

    def __len__(self):
        """Return the size of the sorted list.
        ``sl.__len__()`` <==> ``len(sl)``
        :return: size of sorted list
        """
        return self._len

    def bisect_left(self, value):
        """Return an index to insert `value` in the sorted list.
        If the `value` is already present, the insertion point will be before
        (to the left of) any existing values.
        Similar to the `bisect` module in the standard library.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList([10, 11, 12, 13, 14])
        >>> sl.bisect_left(12)
        2
        :param value: insertion index of value in sorted list
        :return: index
        """
        _maxes = self._maxes

        if not _maxes:
            return 0

        pos = bisect_left(_maxes, value)

        if pos == len(_maxes):
            return self._len

        idx = bisect_left(self._lists[pos], value)
        return self._loc(pos, idx)

    def bisect_right(self, value):
        """Return an index to insert `value` in the sorted list.
        Similar to `bisect_left`, but if `value` is already present, the
        insertion point will be after (to the right of) any existing values.
        Similar to the `bisect` module in the standard library.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList([10, 11, 12, 13, 14])
        >>> sl.bisect_right(12)
        3
        :param value: insertion index of value in sorted list
        :return: index
        """
        _maxes = self._maxes

        if not _maxes:
            return 0

        pos = bisect_right(_maxes, value)

        if pos == len(_maxes):
            return self._len

        idx = bisect_right(self._lists[pos], value)
        return self._loc(pos, idx)

    bisect = bisect_right
    _bisect_right = bisect_right

    def count(self, value):
        """Return number of occurrences of `value` in the sorted list.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList([1, 2, 2, 3, 3, 3, 4, 4, 4, 4])
        >>> sl.count(3)
        3
        :param value: value to count in sorted list
        :return: count
        """
        _maxes = self._maxes

        if not _maxes:
            return 0

        pos_left = bisect_left(_maxes, value)

        if pos_left == len(_maxes):
            return 0

        _lists = self._lists
        idx_left = bisect_left(_lists[pos_left], value)
        pos_right = bisect_right(_maxes, value)

        if pos_right == len(_maxes):
            return self._len - self._loc(pos_left, idx_left)

        idx_right = bisect_right(_lists[pos_right], value)

        if pos_left == pos_right:
            return idx_right - idx_left

        right = self._loc(pos_right, idx_right)
        left = self._loc(pos_left, idx_left)
        return right - left

    def copy(self):
        """Return a shallow copy of the sorted list.
        Runtime complexity: `O(n)`
        :return: new sorted list
        """
        return self.__class__(self)

    __copy__ = copy

    def append(self, value):
        """Raise not-implemented error.
        Implemented to override `MutableSequence.append` which provides an
        erroneous default implementation.
        :raises NotImplementedError: use ``sl.add(value)`` instead
        """
        raise NotImplementedError('use ``sl.add(value)`` instead')

    def extend(self, values):
        """Raise not-implemented error.
        Implemented to override `MutableSequence.extend` which provides an
        erroneous default implementation.
        :raises NotImplementedError: use ``sl.update(values)`` instead
        """
        raise NotImplementedError('use ``sl.update(values)`` instead')

    def insert(self, index, value):
        """Raise not-implemented error.
        :raises NotImplementedError: use ``sl.add(value)`` instead
        """
        raise NotImplementedError('use ``sl.add(value)`` instead')

    def pop(self, index=-1):
        """Remove and return value at `index` in sorted list.
        Raise :exc:`IndexError` if the sorted list is empty or index is out of
        range.
        Negative indices are supported.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList('abcde')
        >>> sl.pop()
        'e'
        >>> sl.pop(2)
        'c'
        >>> sl
        SortedList(['a', 'b', 'd'])
        :param int index: index of value (default -1)
        :return: value
        :raises IndexError: if index is out of range
        """
        if not self._len:
            raise IndexError('pop index out of range')

        _lists = self._lists

        if index == 0:
            val = _lists[0][0]
            self._delete(0, 0)
            return val

        if index == -1:
            pos = len(_lists) - 1
            loc = len(_lists[pos]) - 1
            val = _lists[pos][loc]
            self._delete(pos, loc)
            return val

        if 0 <= index < len(_lists[0]):
            val = _lists[0][index]
            self._delete(0, index)
            return val

        len_last = len(_lists[-1])

        if -len_last < index < 0:
            pos = len(_lists) - 1
            loc = len_last + index
            val = _lists[pos][loc]
            self._delete(pos, loc)
            return val

        pos, idx = self._pos(index)
        val = _lists[pos][idx]
        self._delete(pos, idx)
        return val

    def index(self, value, start=None, stop=None):
        """Return first index of value in sorted list.
        Raise ValueError if `value` is not present.
        Index must be between `start` and `stop` for the `value` to be
        considered present. The default value, None, for `start` and `stop`
        indicate the beginning and end of the sorted list.
        Negative indices are supported.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sl = SortedList('abcde')
        >>> sl.index('d')
        3
        >>> sl.index('z')
        Traceback (most recent call last):
          ...
        ValueError: 'z' is not in list
        :param value: value in sorted list
        :param int start: start index (default None, start of sorted list)
        :param int stop: stop index (default None, end of sorted list)
        :return: index of value
        :raises ValueError: if value is not present
        """
        _len = self._len

        if not _len:
            raise ValueError('{0!r} is not in list'.format(value))

        if start is None:
            start = 0
        if start < 0:
            start += _len
        if start < 0:
            start = 0

        if stop is None:
            stop = _len
        if stop < 0:
            stop += _len
        if stop > _len:
            stop = _len

        if stop <= start:
            raise ValueError('{0!r} is not in list'.format(value))

        _maxes = self._maxes
        pos_left = bisect_left(_maxes, value)

        if pos_left == len(_maxes):
            raise ValueError('{0!r} is not in list'.format(value))

        _lists = self._lists
        idx_left = bisect_left(_lists[pos_left], value)

        if _lists[pos_left][idx_left] != value:
            raise ValueError('{0!r} is not in list'.format(value))

        stop -= 1
        left = self._loc(pos_left, idx_left)

        if start <= left:
            if left <= stop:
                return left
        else:
            right = self._bisect_right(value) - 1

            if start <= right:
                return start

        raise ValueError('{0!r} is not in list'.format(value))

    def __add__(self, other):
        """Return new sorted list containing all values in both sequences.
        ``sl.__add__(other)`` <==> ``sl + other``
        Values in `other` do not need to be in sorted order.
        Runtime complexity: `O(n*log(n))`
        >>> sl1 = SortedList('bat')
        >>> sl2 = SortedList('cat')
        >>> sl1 + sl2
        SortedList(['a', 'a', 'b', 'c', 't', 't'])
        :param other: other iterable
        :return: new sorted list
        """
        values = reduce(iadd, self._lists, [])
        values.extend(other)
        return self.__class__(values)

    __radd__ = __add__

    def __iadd__(self, other):
        """Update sorted list with values from `other`.
        ``sl.__iadd__(other)`` <==> ``sl += other``
        Values in `other` do not need to be in sorted order.
        Runtime complexity: `O(k*log(n))` -- approximate.
        >>> sl = SortedList('bat')
        >>> sl += 'cat'
        >>> sl
        SortedList(['a', 'a', 'b', 'c', 't', 't'])
        :param other: other iterable
        :return: existing sorted list
        """
        self._update(other)
        return self

    def __mul__(self, num):
        """Return new sorted list with `num` shallow copies of values.
        ``sl.__mul__(num)`` <==> ``sl * num``
        Runtime complexity: `O(n*log(n))`
        >>> sl = SortedList('abc')
        >>> sl * 3
        SortedList(['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'])
        :param int num: count of shallow copies
        :return: new sorted list
        """
        values = reduce(iadd, self._lists, []) * num
        return self.__class__(values)

    __rmul__ = __mul__

    def __imul__(self, num):
        """Update the sorted list with `num` shallow copies of values.
        ``sl.__imul__(num)`` <==> ``sl *= num``
        Runtime complexity: `O(n*log(n))`
        >>> sl = SortedList('abc')
        >>> sl *= 3
        >>> sl
        SortedList(['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'])
        :param int num: count of shallow copies
        :return: existing sorted list
        """
        values = reduce(iadd, self._lists, []) * num
        self._clear()
        self._update(values)
        return self

    def __make_cmp(seq_op, symbol, doc):
        "Make comparator method."

        def comparer(self, other):
            "Compare method for sorted list and sequence."
            if not isinstance(other, Sequence):
                return NotImplemented

            self_len = self._len
            len_other = len(other)

            if self_len != len_other:
                if seq_op is eq:
                    return False
                if seq_op is ne:
                    return True

            for alpha, beta in zip(self, other):
                if alpha != beta:
                    return seq_op(alpha, beta)

            return seq_op(self_len, len_other)

        seq_op_name = seq_op.__name__
        comparer.__name__ = '__{0}__'.format(seq_op_name)
        doc_str = """Return true if and only if sorted list is {0} `other`.
        ``sl.__{1}__(other)`` <==> ``sl {2} other``
        Comparisons use lexicographical order as with sequences.
        Runtime complexity: `O(n)`
        :param other: `other` sequence
        :return: true if sorted list is {0} `other`
        """
        comparer.__doc__ = dedent(doc_str.format(doc, seq_op_name, symbol))
        return comparer

    __eq__ = __make_cmp(eq, '==', 'equal to')
    __ne__ = __make_cmp(ne, '!=', 'not equal to')
    __lt__ = __make_cmp(lt, '<', 'less than')
    __gt__ = __make_cmp(gt, '>', 'greater than')
    __le__ = __make_cmp(le, '<=', 'less than or equal to')
    __ge__ = __make_cmp(ge, '>=', 'greater than or equal to')
    __make_cmp = staticmethod(__make_cmp)

    def __reduce__(self):
        values = reduce(iadd, self._lists, [])
        return (type(self), (values,))

    @recursive_repr()
    def __repr__(self):
        """Return string representation of sorted list.
        ``sl.__repr__()`` <==> ``repr(sl)``
        :return: string representation
        """
        return '{0}({1!r})'.format(type(self).__name__, list(self))

    def _check(self):
        """Check invariants of sorted list.
        Runtime complexity: `O(n)`
        """
        try:
            assert self._load >= 4
            assert len(self._maxes) == len(self._lists)
            assert self._len == sum(len(sublist) for sublist in self._lists)

            # Check all sublists are sorted.

            for sublist in self._lists:
                for pos in range(1, len(sublist)):
                    assert sublist[pos - 1] <= sublist[pos]

            # Check beginning/end of sublists are sorted.

            for pos in range(1, len(self._lists)):
                assert self._lists[pos - 1][-1] <= self._lists[pos][0]

            # Check _maxes index is the last value of each sublist.

            for pos in range(len(self._maxes)):
                assert self._maxes[pos] == self._lists[pos][-1]

            # Check sublist lengths are less than double load-factor.

            double = self._load << 1
            assert all(len(sublist) <= double for sublist in self._lists)

            # Check sublist lengths are greater than half load-factor for all
            # but the last sublist.

            half = self._load >> 1
            for pos in range(0, len(self._lists) - 1):
                assert len(self._lists[pos]) >= half

            if self._index:
                assert self._len == self._index[0]
                assert len(self._index) == self._offset + len(self._lists)

                # Check index leaf nodes equal length of sublists.

                for pos in range(len(self._lists)):
                    leaf = self._index[self._offset + pos]
                    assert leaf == len(self._lists[pos])

                # Check index branch nodes are the sum of their children.

                for pos in range(self._offset):
                    child = (pos << 1) + 1
                    if child >= len(self._index):
                        assert self._index[pos] == 0
                    elif child + 1 == len(self._index):
                        assert self._index[pos] == self._index[child]
                    else:
                        child_sum = self._index[child] + self._index[child + 1]
                        assert child_sum == self._index[pos]
        except BaseException:
            traceback.print_exc(file=sys.stdout)
            print('len', self._len)
            print('load', self._load)
            print('offset', self._offset)
            print('len_index', len(self._index))
            print('index', self._index)
            print('len_maxes', len(self._maxes))
            print('maxes', self._maxes)
            print('len_lists', len(self._lists))
            print('lists', self._lists)
            raise


def identity(value):
    "Identity function."
    return value


class SortedKeyList(SortedList):
    """Sorted-key list is a subtype of sorted list.
    The sorted-key list maintains values in comparison order based on the
    result of a key function applied to every value.
    All the same methods that are available in :class:`SortedList` are also
    available in :class:`SortedKeyList`.
    Additional methods provided:
    * :attr:`SortedKeyList.key`
    * :func:`SortedKeyList.bisect_key_left`
    * :func:`SortedKeyList.bisect_key_right`
    * :func:`SortedKeyList.irange_key`
    Some examples below use:
    >>> from operator import neg
    >>> neg
    <built-in function neg>
    >>> neg(1)
    -1
    """

    def __init__(self, iterable=None, key=identity):
        """Initialize sorted-key list instance.
        Optional `iterable` argument provides an initial iterable of values to
        initialize the sorted-key list.
        Optional `key` argument defines a callable that, like the `key`
        argument to Python's `sorted` function, extracts a comparison key from
        each value. The default is the identity function.
        Runtime complexity: `O(n*log(n))`
        >>> from operator import neg
        >>> skl = SortedKeyList(key=neg)
        >>> skl
        SortedKeyList([], key=<built-in function neg>)
        >>> skl = SortedKeyList([3, 1, 2], key=neg)
        >>> skl
        SortedKeyList([3, 2, 1], key=<built-in function neg>)
        :param iterable: initial values (optional)
        :param key: function used to extract comparison key (optional)
        """
        self._key = key
        self._len = 0
        self._load = self.DEFAULT_LOAD_FACTOR
        self._lists = []
        self._keys = []
        self._maxes = []
        self._index = []
        self._offset = 0

        if iterable is not None:
            self._update(iterable)

    def __new__(cls, iterable=None, key=identity):
        return object.__new__(cls)

    @property
    def key(self):
        "Function used to extract comparison key from values."
        return self._key

    def clear(self):
        """Remove all values from sorted-key list.
        Runtime complexity: `O(n)`
        """
        self._len = 0
        del self._lists[:]
        del self._keys[:]
        del self._maxes[:]
        del self._index[:]

    _clear = clear

    def add(self, value):
        """Add `value` to sorted-key list.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList(key=neg)
        >>> skl.add(3)
        >>> skl.add(1)
        >>> skl.add(2)
        >>> skl
        SortedKeyList([3, 2, 1], key=<built-in function neg>)
        :param value: value to add to sorted-key list
        """
        _lists = self._lists
        _keys = self._keys
        _maxes = self._maxes

        key = self._key(value)

        if _maxes:
            pos = bisect_right(_maxes, key)

            if pos == len(_maxes):
                pos -= 1
                _lists[pos].append(value)
                _keys[pos].append(key)
                _maxes[pos] = key
            else:
                idx = bisect_right(_keys[pos], key)
                _lists[pos].insert(idx, value)
                _keys[pos].insert(idx, key)

            self._expand(pos)
        else:
            _lists.append([value])
            _keys.append([key])
            _maxes.append(key)

        self._len += 1

    def _expand(self, pos):
        """Split sublists with length greater than double the load-factor.
        Updates the index when the sublist length is less than double the load
        level. This requires incrementing the nodes in a traversal from the
        leaf node to the root. For an example traversal see
        ``SortedList._loc``.
        """
        _lists = self._lists
        _keys = self._keys
        _index = self._index

        if len(_keys[pos]) > (self._load << 1):
            _maxes = self._maxes
            _load = self._load

            _lists_pos = _lists[pos]
            _keys_pos = _keys[pos]
            half = _lists_pos[_load:]
            half_keys = _keys_pos[_load:]
            del _lists_pos[_load:]
            del _keys_pos[_load:]
            _maxes[pos] = _keys_pos[-1]

            _lists.insert(pos + 1, half)
            _keys.insert(pos + 1, half_keys)
            _maxes.insert(pos + 1, half_keys[-1])

            del _index[:]
        else:
            if _index:
                child = self._offset + pos
                while child:
                    _index[child] += 1
                    child = (child - 1) >> 1
                _index[0] += 1

    def update(self, iterable):
        """Update sorted-key list by adding all values from `iterable`.
        Runtime complexity: `O(k*log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList(key=neg)
        >>> skl.update([3, 1, 2])
        >>> skl
        SortedKeyList([3, 2, 1], key=<built-in function neg>)
        :param iterable: iterable of values to add
        """
        _lists = self._lists
        _keys = self._keys
        _maxes = self._maxes
        values = sorted(iterable, key=self._key)

        if _maxes:
            if len(values) * 4 >= self._len:
                values.extend(chain.from_iterable(_lists))
                values.sort(key=self._key)
                self._clear()
            else:
                _add = self.add
                for val in values:
                    _add(val)
                return

        _load = self._load
        _lists.extend(values[pos:(pos + _load)]
                      for pos in range(0, len(values), _load))
        _keys.extend(list(map(self._key, _list)) for _list in _lists)
        _maxes.extend(sublist[-1] for sublist in _keys)
        self._len = len(values)
        del self._index[:]

    _update = update

    def __contains__(self, value):
        """Return true if `value` is an element of the sorted-key list.
        ``skl.__contains__(value)`` <==> ``value in skl``
        Runtime complexity: `O(log(n))`
        >>> from operator import neg
        >>> skl = SortedKeyList([1, 2, 3, 4, 5], key=neg)
        >>> 3 in skl
        True
        :param value: search for value in sorted-key list
        :return: true if `value` in sorted-key list
        """
        _maxes = self._maxes

        if not _maxes:
            return False

        key = self._key(value)
        pos = bisect_left(_maxes, key)

        if pos == len(_maxes):
            return False

        _lists = self._lists
        _keys = self._keys

        idx = bisect_left(_keys[pos], key)

        len_keys = len(_keys)
        len_sublist = len(_keys[pos])

        while True:
            if _keys[pos][idx] != key:
                return False
            if _lists[pos][idx] == value:
                return True
            idx += 1
            if idx == len_sublist:
                pos += 1
                if pos == len_keys:
                    return False
                len_sublist = len(_keys[pos])
                idx = 0

    def discard(self, value):
        """Remove `value` from sorted-key list if it is a member.
        If `value` is not a member, do nothing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg)
        >>> skl.discard(1)
        >>> skl.discard(0)
        >>> skl == [5, 4, 3, 2]
        True
        :param value: `value` to discard from sorted-key list
        """
        _maxes = self._maxes

        if not _maxes:
            return

        key = self._key(value)
        pos = bisect_left(_maxes, key)

        if pos == len(_maxes):
            return

        _lists = self._lists
        _keys = self._keys
        idx = bisect_left(_keys[pos], key)
        len_keys = len(_keys)
        len_sublist = len(_keys[pos])

        while True:
            if _keys[pos][idx] != key:
                return
            if _lists[pos][idx] == value:
                self._delete(pos, idx)
                return
            idx += 1
            if idx == len_sublist:
                pos += 1
                if pos == len_keys:
                    return
                len_sublist = len(_keys[pos])
                idx = 0

    def remove(self, value):
        """Remove `value` from sorted-key list; `value` must be a member.
        If `value` is not a member, raise ValueError.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList([1, 2, 3, 4, 5], key=neg)
        >>> skl.remove(5)
        >>> skl == [4, 3, 2, 1]
        True
        >>> skl.remove(0)
        Traceback (most recent call last):
          ...
        ValueError: 0 not in list
        :param value: `value` to remove from sorted-key list
        :raises ValueError: if `value` is not in sorted-key list
        """
        _maxes = self._maxes

        if not _maxes:
            raise ValueError('{0!r} not in list'.format(value))

        key = self._key(value)
        pos = bisect_left(_maxes, key)

        if pos == len(_maxes):
            raise ValueError('{0!r} not in list'.format(value))

        _lists = self._lists
        _keys = self._keys
        idx = bisect_left(_keys[pos], key)
        len_keys = len(_keys)
        len_sublist = len(_keys[pos])

        while True:
            if _keys[pos][idx] != key:
                raise ValueError('{0!r} not in list'.format(value))
            if _lists[pos][idx] == value:
                self._delete(pos, idx)
                return
            idx += 1
            if idx == len_sublist:
                pos += 1
                if pos == len_keys:
                    raise ValueError('{0!r} not in list'.format(value))
                len_sublist = len(_keys[pos])
                idx = 0

    def _delete(self, pos, idx):
        """Delete value at the given `(pos, idx)`.
        Combines lists that are less than half the load level.
        Updates the index when the sublist length is more than half the load
        level. This requires decrementing the nodes in a traversal from the
        leaf node to the root. For an example traversal see
        ``SortedList._loc``.
        :param int pos: lists index
        :param int idx: sublist index
        """
        _lists = self._lists
        _keys = self._keys
        _maxes = self._maxes
        _index = self._index
        keys_pos = _keys[pos]
        lists_pos = _lists[pos]

        del keys_pos[idx]
        del lists_pos[idx]
        self._len -= 1

        len_keys_pos = len(keys_pos)

        if len_keys_pos > (self._load >> 1):
            _maxes[pos] = keys_pos[-1]

            if _index:
                child = self._offset + pos
                while child > 0:
                    _index[child] -= 1
                    child = (child - 1) >> 1
                _index[0] -= 1
        elif len(_keys) > 1:
            if not pos:
                pos += 1

            prev = pos - 1
            _keys[prev].extend(_keys[pos])
            _lists[prev].extend(_lists[pos])
            _maxes[prev] = _keys[prev][-1]

            del _lists[pos]
            del _keys[pos]
            del _maxes[pos]
            del _index[:]

            self._expand(prev)
        elif len_keys_pos:
            _maxes[pos] = keys_pos[-1]
        else:
            del _lists[pos]
            del _keys[pos]
            del _maxes[pos]
            del _index[:]

    def irange(self, minimum=None, maximum=None, inclusive=(True, True),
               reverse=False):
        """Create an iterator of values between `minimum` and `maximum`.
        Both `minimum` and `maximum` default to `None` which is automatically
        inclusive of the beginning and end of the sorted-key list.
        The argument `inclusive` is a pair of booleans that indicates whether
        the minimum and maximum ought to be included in the range,
        respectively. The default is ``(True, True)`` such that the range is
        inclusive of both minimum and maximum.
        When `reverse` is `True` the values are yielded from the iterator in
        reverse order; `reverse` defaults to `False`.
        >>> from operator import neg
        >>> skl = SortedKeyList([11, 12, 13, 14, 15], key=neg)
        >>> it = skl.irange(14.5, 11.5)
        >>> list(it)
        [14, 13, 12]
        :param minimum: minimum value to start iterating
        :param maximum: maximum value to stop iterating
        :param inclusive: pair of booleans
        :param bool reverse: yield values in reverse order
        :return: iterator
        """
        min_key = self._key(minimum) if minimum is not None else None
        max_key = self._key(maximum) if maximum is not None else None
        return self._irange_key(
            min_key=min_key, max_key=max_key,
            inclusive=inclusive, reverse=reverse,
        )

    def irange_key(self, min_key=None, max_key=None, inclusive=(True, True),
                   reverse=False):
        """Create an iterator of values between `min_key` and `max_key`.
        Both `min_key` and `max_key` default to `None` which is automatically
        inclusive of the beginning and end of the sorted-key list.
        The argument `inclusive` is a pair of booleans that indicates whether
        the minimum and maximum ought to be included in the range,
        respectively. The default is ``(True, True)`` such that the range is
        inclusive of both minimum and maximum.
        When `reverse` is `True` the values are yielded from the iterator in
        reverse order; `reverse` defaults to `False`.
        >>> from operator import neg
        >>> skl = SortedKeyList([11, 12, 13, 14, 15], key=neg)
        >>> it = skl.irange_key(-14, -12)
        >>> list(it)
        [14, 13, 12]
        :param min_key: minimum key to start iterating
        :param max_key: maximum key to stop iterating
        :param inclusive: pair of booleans
        :param bool reverse: yield values in reverse order
        :return: iterator
        """
        _maxes = self._maxes

        if not _maxes:
            return iter(())

        _keys = self._keys

        # Calculate the minimum (pos, idx) pair. By default this location
        # will be inclusive in our calculation.

        if min_key is None:
            min_pos = 0
            min_idx = 0
        else:
            if inclusive[0]:
                min_pos = bisect_left(_maxes, min_key)

                if min_pos == len(_maxes):
                    return iter(())

                min_idx = bisect_left(_keys[min_pos], min_key)
            else:
                min_pos = bisect_right(_maxes, min_key)

                if min_pos == len(_maxes):
                    return iter(())

                min_idx = bisect_right(_keys[min_pos], min_key)

        # Calculate the maximum (pos, idx) pair. By default this location
        # will be exclusive in our calculation.

        if max_key is None:
            max_pos = len(_maxes) - 1
            max_idx = len(_keys[max_pos])
        else:
            if inclusive[1]:
                max_pos = bisect_right(_maxes, max_key)

                if max_pos == len(_maxes):
                    max_pos -= 1
                    max_idx = len(_keys[max_pos])
                else:
                    max_idx = bisect_right(_keys[max_pos], max_key)
            else:
                max_pos = bisect_left(_maxes, max_key)

                if max_pos == len(_maxes):
                    max_pos -= 1
                    max_idx = len(_keys[max_pos])
                else:
                    max_idx = bisect_left(_keys[max_pos], max_key)

        return self._islice(min_pos, min_idx, max_pos, max_idx, reverse)

    _irange_key = irange_key

    def bisect_left(self, value):
        """Return an index to insert `value` in the sorted-key list.
        If the `value` is already present, the insertion point will be before
        (to the left of) any existing values.
        Similar to the `bisect` module in the standard library.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg)
        >>> skl.bisect_left(1)
        4
        :param value: insertion index of value in sorted-key list
        :return: index
        """
        return self._bisect_key_left(self._key(value))

    def bisect_right(self, value):
        """Return an index to insert `value` in the sorted-key list.
        Similar to `bisect_left`, but if `value` is already present, the
        insertion point will be after (to the right of) any existing values.
        Similar to the `bisect` module in the standard library.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedList([5, 4, 3, 2, 1], key=neg)
        >>> skl.bisect_right(1)
        5
        :param value: insertion index of value in sorted-key list
        :return: index
        """
        return self._bisect_key_right(self._key(value))

    bisect = bisect_right

    def bisect_key_left(self, key):
        """Return an index to insert `key` in the sorted-key list.
        If the `key` is already present, the insertion point will be before (to
        the left of) any existing keys.
        Similar to the `bisect` module in the standard library.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg)
        >>> skl.bisect_key_left(-1)
        4
        :param key: insertion index of key in sorted-key list
        :return: index
        """
        _maxes = self._maxes

        if not _maxes:
            return 0

        pos = bisect_left(_maxes, key)

        if pos == len(_maxes):
            return self._len

        idx = bisect_left(self._keys[pos], key)

        return self._loc(pos, idx)

    _bisect_key_left = bisect_key_left

    def bisect_key_right(self, key):
        """Return an index to insert `key` in the sorted-key list.
        Similar to `bisect_key_left`, but if `key` is already present, the
        insertion point will be after (to the right of) any existing keys.
        Similar to the `bisect` module in the standard library.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedList([5, 4, 3, 2, 1], key=neg)
        >>> skl.bisect_key_right(-1)
        5
        :param key: insertion index of key in sorted-key list
        :return: index
        """
        _maxes = self._maxes

        if not _maxes:
            return 0

        pos = bisect_right(_maxes, key)

        if pos == len(_maxes):
            return self._len

        idx = bisect_right(self._keys[pos], key)

        return self._loc(pos, idx)

    bisect_key = bisect_key_right
    _bisect_key_right = bisect_key_right

    def count(self, value):
        """Return number of occurrences of `value` in the sorted-key list.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList([4, 4, 4, 4, 3, 3, 3, 2, 2, 1], key=neg)
        >>> skl.count(2)
        2
        :param value: value to count in sorted-key list
        :return: count
        """
        _maxes = self._maxes

        if not _maxes:
            return 0

        key = self._key(value)
        pos = bisect_left(_maxes, key)

        if pos == len(_maxes):
            return 0

        _lists = self._lists
        _keys = self._keys
        idx = bisect_left(_keys[pos], key)
        total = 0
        len_keys = len(_keys)
        len_sublist = len(_keys[pos])

        while True:
            if _keys[pos][idx] != key:
                return total
            if _lists[pos][idx] == value:
                total += 1
            idx += 1
            if idx == len_sublist:
                pos += 1
                if pos == len_keys:
                    return total
                len_sublist = len(_keys[pos])
                idx = 0

    def copy(self):
        """Return a shallow copy of the sorted-key list.
        Runtime complexity: `O(n)`
        :return: new sorted-key list
        """
        return self.__class__(self, key=self._key)

    __copy__ = copy

    def index(self, value, start=None, stop=None):
        """Return first index of value in sorted-key list.
        Raise ValueError if `value` is not present.
        Index must be between `start` and `stop` for the `value` to be
        considered present. The default value, None, for `start` and `stop`
        indicate the beginning and end of the sorted-key list.
        Negative indices are supported.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> from operator import neg
        >>> skl = SortedKeyList([5, 4, 3, 2, 1], key=neg)
        >>> skl.index(2)
        3
        >>> skl.index(0)
        Traceback (most recent call last):
          ...
        ValueError: 0 is not in list
        :param value: value in sorted-key list
        :param int start: start index (default None, start of sorted-key list)
        :param int stop: stop index (default None, end of sorted-key list)
        :return: index of value
        :raises ValueError: if value is not present
        """
        _len = self._len

        if not _len:
            raise ValueError('{0!r} is not in list'.format(value))

        if start is None:
            start = 0
        if start < 0:
            start += _len
        if start < 0:
            start = 0

        if stop is None:
            stop = _len
        if stop < 0:
            stop += _len
        if stop > _len:
            stop = _len

        if stop <= start:
            raise ValueError('{0!r} is not in list'.format(value))

        _maxes = self._maxes
        key = self._key(value)
        pos = bisect_left(_maxes, key)

        if pos == len(_maxes):
            raise ValueError('{0!r} is not in list'.format(value))

        stop -= 1
        _lists = self._lists
        _keys = self._keys
        idx = bisect_left(_keys[pos], key)
        len_keys = len(_keys)
        len_sublist = len(_keys[pos])

        while True:
            if _keys[pos][idx] != key:
                raise ValueError('{0!r} is not in list'.format(value))
            if _lists[pos][idx] == value:
                loc = self._loc(pos, idx)
                if start <= loc <= stop:
                    return loc
                elif loc > stop:
                    break
            idx += 1
            if idx == len_sublist:
                pos += 1
                if pos == len_keys:
                    raise ValueError('{0!r} is not in list'.format(value))
                len_sublist = len(_keys[pos])
                idx = 0

        raise ValueError('{0!r} is not in list'.format(value))

    def __add__(self, other):
        """Return new sorted-key list containing all values in both sequences.
        ``skl.__add__(other)`` <==> ``skl + other``
        Values in `other` do not need to be in sorted-key order.
        Runtime complexity: `O(n*log(n))`
        >>> from operator import neg
        >>> skl1 = SortedKeyList([5, 4, 3], key=neg)
        >>> skl2 = SortedKeyList([2, 1, 0], key=neg)
        >>> skl1 + skl2
        SortedKeyList([5, 4, 3, 2, 1, 0], key=<built-in function neg>)
        :param other: other iterable
        :return: new sorted-key list
        """
        values = reduce(iadd, self._lists, [])
        values.extend(other)
        return self.__class__(values, key=self._key)

    __radd__ = __add__

    def __mul__(self, num):
        """Return new sorted-key list with `num` shallow copies of values.
        ``skl.__mul__(num)`` <==> ``skl * num``
        Runtime complexity: `O(n*log(n))`
        >>> from operator import neg
        >>> skl = SortedKeyList([3, 2, 1], key=neg)
        >>> skl * 2
        SortedKeyList([3, 3, 2, 2, 1, 1], key=<built-in function neg>)
        :param int num: count of shallow copies
        :return: new sorted-key list
        """
        values = reduce(iadd, self._lists, []) * num
        return self.__class__(values, key=self._key)

    def __reduce__(self):
        values = reduce(iadd, self._lists, [])
        return (type(self), (values, self.key))

    @recursive_repr()
    def __repr__(self):
        """Return string representation of sorted-key list.
        ``skl.__repr__()`` <==> ``repr(skl)``
        :return: string representation
        """
        type_name = type(self).__name__
        return '{0}({1!r}, key={2!r})'.format(type_name, list(self), self._key)

    def _check(self):
        """Check invariants of sorted-key list.
        Runtime complexity: `O(n)`
        """
        try:
            assert self._load >= 4
            assert len(self._maxes) == len(self._lists) == len(self._keys)
            assert self._len == sum(len(sublist) for sublist in self._lists)

            # Check all sublists are sorted.

            for sublist in self._keys:
                for pos in range(1, len(sublist)):
                    assert sublist[pos - 1] <= sublist[pos]

            # Check beginning/end of sublists are sorted.

            for pos in range(1, len(self._keys)):
                assert self._keys[pos - 1][-1] <= self._keys[pos][0]

            # Check _keys matches _key mapped to _lists.

            for val_sublist, key_sublist in zip(self._lists, self._keys):
                assert len(val_sublist) == len(key_sublist)
                for val, key in zip(val_sublist, key_sublist):
                    assert self._key(val) == key

            # Check _maxes index is the last value of each sublist.

            for pos in range(len(self._maxes)):
                assert self._maxes[pos] == self._keys[pos][-1]

            # Check sublist lengths are less than double load-factor.

            double = self._load << 1
            assert all(len(sublist) <= double for sublist in self._lists)

            # Check sublist lengths are greater than half load-factor for all
            # but the last sublist.

            half = self._load >> 1
            for pos in range(0, len(self._lists) - 1):
                assert len(self._lists[pos]) >= half

            if self._index:
                assert self._len == self._index[0]
                assert len(self._index) == self._offset + len(self._lists)

                # Check index leaf nodes equal length of sublists.

                for pos in range(len(self._lists)):
                    leaf = self._index[self._offset + pos]
                    assert leaf == len(self._lists[pos])

                # Check index branch nodes are the sum of their children.

                for pos in range(self._offset):
                    child = (pos << 1) + 1
                    if child >= len(self._index):
                        assert self._index[pos] == 0
                    elif child + 1 == len(self._index):
                        assert self._index[pos] == self._index[child]
                    else:
                        child_sum = self._index[child] + self._index[child + 1]
                        assert child_sum == self._index[pos]
        except BaseException:
            traceback.print_exc(file=sys.stdout)
            print('len', self._len)
            print('load', self._load)
            print('offset', self._offset)
            print('len_index', len(self._index))
            print('index', self._index)
            print('len_maxes', len(self._maxes))
            print('maxes', self._maxes)
            print('len_keys', len(self._keys))
            print('keys', self._keys)
            print('len_lists', len(self._lists))
            print('lists', self._lists)
            raise


class SortedSet(MutableSet, Sequence):
    """Sorted set is a sorted mutable set.
    Sorted set values are maintained in sorted order. The design of sorted set
    is simple: sorted set uses a set for set-operations and maintains a sorted
    list of values.
    Sorted set values must be hashable and comparable. The hash and total
    ordering of values must not change while they are stored in the sorted set.
    Mutable set methods:
    * :func:`SortedSet.__contains__`
    * :func:`SortedSet.__iter__`
    * :func:`SortedSet.__len__`
    * :func:`SortedSet.add`
    * :func:`SortedSet.discard`
    Sequence methods:
    * :func:`SortedSet.__getitem__`
    * :func:`SortedSet.__delitem__`
    * :func:`SortedSet.__reversed__`
    Methods for removing values:
    * :func:`SortedSet.clear`
    * :func:`SortedSet.pop`
    * :func:`SortedSet.remove`
    Set-operation methods:
    * :func:`SortedSet.difference`
    * :func:`SortedSet.difference_update`
    * :func:`SortedSet.intersection`
    * :func:`SortedSet.intersection_update`
    * :func:`SortedSet.symmetric_difference`
    * :func:`SortedSet.symmetric_difference_update`
    * :func:`SortedSet.union`
    * :func:`SortedSet.update`
    Methods for miscellany:
    * :func:`SortedSet.copy`
    * :func:`SortedSet.count`
    * :func:`SortedSet.__repr__`
    * :func:`SortedSet._check`
    Sorted list methods available:
    * :func:`SortedList.bisect_left`
    * :func:`SortedList.bisect_right`
    * :func:`SortedList.index`
    * :func:`SortedList.irange`
    * :func:`SortedList.islice`
    * :func:`SortedList._reset`
    Additional sorted list methods available, if key-function used:
    * :func:`SortedKeyList.bisect_key_left`
    * :func:`SortedKeyList.bisect_key_right`
    * :func:`SortedKeyList.irange_key`
    Sorted set comparisons use subset and superset relations. Two sorted sets
    are equal if and only if every element of each sorted set is contained in
    the other (each is a subset of the other). A sorted set is less than
    another sorted set if and only if the first sorted set is a proper subset
    of the second sorted set (is a subset, but is not equal). A sorted set is
    greater than another sorted set if and only if the first sorted set is a
    proper superset of the second sorted set (is a superset, but is not equal).
    """

    def __init__(self, iterable=None, key=None):
        """Initialize sorted set instance.
        Optional `iterable` argument provides an initial iterable of values to
        initialize the sorted set.
        Optional `key` argument defines a callable that, like the `key`
        argument to Python's `sorted` function, extracts a comparison key from
        each value. The default, none, compares values directly.
        Runtime complexity: `O(n*log(n))`
        >>> ss = SortedSet([3, 1, 2, 5, 4])
        >>> ss
        SortedSet([1, 2, 3, 4, 5])
        >>> from operator import neg
        >>> ss = SortedSet([3, 1, 2, 5, 4], neg)
        >>> ss
        SortedSet([5, 4, 3, 2, 1], key=<built-in function neg>)
        :param iterable: initial values (optional)
        :param key: function used to extract comparison key (optional)
        """
        self._key = key

        # SortedSet._fromset calls SortedSet.__init__ after initializing the
        # _set attribute. So only create a new set if the _set attribute is not
        # already present.

        if not hasattr(self, '_set'):
            self._set = set()

        self._list = SortedList(self._set, key=key)

        # Expose some set methods publicly.

        _set = self._set
        self.isdisjoint = _set.isdisjoint
        self.issubset = _set.issubset
        self.issuperset = _set.issuperset

        # Expose some sorted list methods publicly.

        _list = self._list
        self.bisect_left = _list.bisect_left
        self.bisect = _list.bisect
        self.bisect_right = _list.bisect_right
        self.index = _list.index
        self.irange = _list.irange
        self.islice = _list.islice
        self._reset = _list._reset

        if key is not None:
            self.bisect_key_left = _list.bisect_key_left
            self.bisect_key_right = _list.bisect_key_right
            self.bisect_key = _list.bisect_key
            self.irange_key = _list.irange_key

        if iterable is not None:
            self._update(iterable)

    @classmethod
    def _fromset(cls, values, key=None):
        """Initialize sorted set from existing set.
        Used internally by set operations that return a new set.
        """
        sorted_set = object.__new__(cls)
        sorted_set._set = values
        sorted_set.__init__(key=key)
        return sorted_set

    @property
    def key(self):
        """Function used to extract comparison key from values.
        Sorted set compares values directly when the key function is none.
        """
        return self._key

    def __contains__(self, value):
        """Return true if `value` is an element of the sorted set.
        ``ss.__contains__(value)`` <==> ``value in ss``
        Runtime complexity: `O(1)`
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> 3 in ss
        True
        :param value: search for value in sorted set
        :return: true if `value` in sorted set
        """
        return value in self._set

    def __getitem__(self, index):
        """Lookup value at `index` in sorted set.
        ``ss.__getitem__(index)`` <==> ``ss[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> ss = SortedSet('abcde')
        >>> ss[2]
        'c'
        >>> ss[-1]
        'e'
        >>> ss[2:5]
        ['c', 'd', 'e']
        :param index: integer or slice for indexing
        :return: value or list of values
        :raises IndexError: if index out of range
        """
        return self._list[index]

    def __delitem__(self, index):
        """Remove value at `index` from sorted set.
        ``ss.__delitem__(index)`` <==> ``del ss[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> ss = SortedSet('abcde')
        >>> del ss[2]
        >>> ss
        SortedSet(['a', 'b', 'd', 'e'])
        >>> del ss[:2]
        >>> ss
        SortedSet(['d', 'e'])
        :param index: integer or slice for indexing
        :raises IndexError: if index out of range
        """
        _set = self._set
        _list = self._list
        if isinstance(index, slice):
            values = _list[index]
            _set.difference_update(values)
        else:
            value = _list[index]
            _set.remove(value)
        del _list[index]

    def __make_cmp(set_op, symbol, doc):
        "Make comparator method."

        def comparer(self, other):
            "Compare method for sorted set and set."
            if isinstance(other, SortedSet):
                return set_op(self._set, other._set)
            elif isinstance(other, Set):
                return set_op(self._set, other)
            return NotImplemented

        set_op_name = set_op.__name__
        comparer.__name__ = '__{0}__'.format(set_op_name)
        doc_str = """Return true if and only if sorted set is {0} `other`.
        ``ss.__{1}__(other)`` <==> ``ss {2} other``
        Comparisons use subset and superset semantics as with sets.
        Runtime complexity: `O(n)`
        :param other: `other` set
        :return: true if sorted set is {0} `other`
        """
        comparer.__doc__ = dedent(doc_str.format(doc, set_op_name, symbol))
        return comparer

    __eq__ = __make_cmp(eq, '==', 'equal to')
    __ne__ = __make_cmp(ne, '!=', 'not equal to')
    __lt__ = __make_cmp(lt, '<', 'a proper subset of')
    __gt__ = __make_cmp(gt, '>', 'a proper superset of')
    __le__ = __make_cmp(le, '<=', 'a subset of')
    __ge__ = __make_cmp(ge, '>=', 'a superset of')
    __make_cmp = staticmethod(__make_cmp)

    def __len__(self):
        """Return the size of the sorted set.
        ``ss.__len__()`` <==> ``len(ss)``
        :return: size of sorted set
        """
        return len(self._set)

    def __iter__(self):
        """Return an iterator over the sorted set.
        ``ss.__iter__()`` <==> ``iter(ss)``
        Iterating the sorted set while adding or deleting values may raise a
        :exc:`RuntimeError` or fail to iterate over all values.
        """
        return iter(self._list)

    def __reversed__(self):
        """Return a reverse iterator over the sorted set.
        ``ss.__reversed__()`` <==> ``reversed(ss)``
        Iterating the sorted set while adding or deleting values may raise a
        :exc:`RuntimeError` or fail to iterate over all values.
        """
        return reversed(self._list)

    def add(self, value):
        """Add `value` to sorted set.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> ss = SortedSet()
        >>> ss.add(3)
        >>> ss.add(1)
        >>> ss.add(2)
        >>> ss
        SortedSet([1, 2, 3])
        :param value: value to add to sorted set
        """
        _set = self._set
        if value not in _set:
            _set.add(value)
            self._list.add(value)

    _add = add

    def clear(self):
        """Remove all values from sorted set.
        Runtime complexity: `O(n)`
        """
        self._set.clear()
        self._list.clear()

    def copy(self):
        """Return a shallow copy of the sorted set.
        Runtime complexity: `O(n)`
        :return: new sorted set
        """
        return self._fromset(set(self._set), key=self._key)

    __copy__ = copy

    def count(self, value):
        """Return number of occurrences of `value` in the sorted set.
        Runtime complexity: `O(1)`
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.count(3)
        1
        :param value: value to count in sorted set
        :return: count
        """
        return 1 if value in self._set else 0

    def discard(self, value):
        """Remove `value` from sorted set if it is a member.
        If `value` is not a member, do nothing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.discard(5)
        >>> ss.discard(0)
        >>> ss == set([1, 2, 3, 4])
        True
        :param value: `value` to discard from sorted set
        """
        _set = self._set
        if value in _set:
            _set.remove(value)
            self._list.remove(value)

    _discard = discard

    def pop(self, index=-1):
        """Remove and return value at `index` in sorted set.
        Raise :exc:`IndexError` if the sorted set is empty or index is out of
        range.
        Negative indices are supported.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> ss = SortedSet('abcde')
        >>> ss.pop()
        'e'
        >>> ss.pop(2)
        'c'
        >>> ss
        SortedSet(['a', 'b', 'd'])
        :param int index: index of value (default -1)
        :return: value
        :raises IndexError: if index is out of range
        """
        # pylint: disable=arguments-differ
        value = self._list.pop(index)
        self._set.remove(value)
        return value

    def remove(self, value):
        """Remove `value` from sorted set; `value` must be a member.
        If `value` is not a member, raise :exc:`KeyError`.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.remove(5)
        >>> ss == set([1, 2, 3, 4])
        True
        >>> ss.remove(0)
        Traceback (most recent call last):
          ...
        KeyError: 0
        :param value: `value` to remove from sorted set
        :raises KeyError: if `value` is not in sorted set
        """
        self._set.remove(value)
        self._list.remove(value)

    def difference(self, *iterables):
        """Return the difference of two or more sets as a new sorted set.
        The `difference` method also corresponds to operator ``-``.
        ``ss.__sub__(iterable)`` <==> ``ss - iterable``
        The difference is all values that are in this sorted set but not the
        other `iterables`.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.difference([4, 5, 6, 7])
        SortedSet([1, 2, 3])
        :param iterables: iterable arguments
        :return: new sorted set
        """
        diff = self._set.difference(*iterables)
        return self._fromset(diff, key=self._key)

    __sub__ = difference

    def difference_update(self, *iterables):
        """Remove all values of `iterables` from this sorted set.
        The `difference_update` method also corresponds to operator ``-=``.
        ``ss.__isub__(iterable)`` <==> ``ss -= iterable``
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> _ = ss.difference_update([4, 5, 6, 7])
        >>> ss
        SortedSet([1, 2, 3])
        :param iterables: iterable arguments
        :return: itself
        """
        _set = self._set
        _list = self._list
        values = set(chain(*iterables))
        if (4 * len(values)) > len(_set):
            _set.difference_update(values)
            _list.clear()
            _list.update(_set)
        else:
            _discard = self._discard
            for value in values:
                _discard(value)
        return self

    __isub__ = difference_update

    def intersection(self, *iterables):
        """Return the intersection of two or more sets as a new sorted set.
        The `intersection` method also corresponds to operator ``&``.
        ``ss.__and__(iterable)`` <==> ``ss & iterable``
        The intersection is all values that are in this sorted set and each of
        the other `iterables`.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.intersection([4, 5, 6, 7])
        SortedSet([4, 5])
        :param iterables: iterable arguments
        :return: new sorted set
        """
        intersect = self._set.intersection(*iterables)
        return self._fromset(intersect, key=self._key)

    __and__ = intersection
    __rand__ = __and__

    def intersection_update(self, *iterables):
        """Update the sorted set with the intersection of `iterables`.
        The `intersection_update` method also corresponds to operator ``&=``.
        ``ss.__iand__(iterable)`` <==> ``ss &= iterable``
        Keep only values found in itself and all `iterables`.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> _ = ss.intersection_update([4, 5, 6, 7])
        >>> ss
        SortedSet([4, 5])
        :param iterables: iterable arguments
        :return: itself
        """
        _set = self._set
        _list = self._list
        _set.intersection_update(*iterables)
        _list.clear()
        _list.update(_set)
        return self

    __iand__ = intersection_update

    def symmetric_difference(self, other):
        """Return the symmetric difference with `other` as a new sorted set.
        The `symmetric_difference` method also corresponds to operator ``^``.
        ``ss.__xor__(other)`` <==> ``ss ^ other``
        The symmetric difference is all values tha are in exactly one of the
        sets.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.symmetric_difference([4, 5, 6, 7])
        SortedSet([1, 2, 3, 6, 7])
        :param other: `other` iterable
        :return: new sorted set
        """
        diff = self._set.symmetric_difference(other)
        return self._fromset(diff, key=self._key)

    __xor__ = symmetric_difference
    __rxor__ = __xor__

    def symmetric_difference_update(self, other):
        """Update the sorted set with the symmetric difference with `other`.
        The `symmetric_difference_update` method also corresponds to operator
        ``^=``.
        ``ss.__ixor__(other)`` <==> ``ss ^= other``
        Keep only values found in exactly one of itself and `other`.
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> _ = ss.symmetric_difference_update([4, 5, 6, 7])
        >>> ss
        SortedSet([1, 2, 3, 6, 7])
        :param other: `other` iterable
        :return: itself
        """
        _set = self._set
        _list = self._list
        _set.symmetric_difference_update(other)
        _list.clear()
        _list.update(_set)
        return self

    __ixor__ = symmetric_difference_update

    def union(self, *iterables):
        """Return new sorted set with values from itself and all `iterables`.
        The `union` method also corresponds to operator ``|``.
        ``ss.__or__(iterable)`` <==> ``ss | iterable``
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> ss.union([4, 5, 6, 7])
        SortedSet([1, 2, 3, 4, 5, 6, 7])
        :param iterables: iterable arguments
        :return: new sorted set
        """
        return self.__class__(chain(iter(self), *iterables), key=self._key)

    __or__ = union
    __ror__ = __or__

    def update(self, *iterables):
        """Update the sorted set adding values from all `iterables`.
        The `update` method also corresponds to operator ``|=``.
        ``ss.__ior__(iterable)`` <==> ``ss |= iterable``
        >>> ss = SortedSet([1, 2, 3, 4, 5])
        >>> _ = ss.update([4, 5, 6, 7])
        >>> ss
        SortedSet([1, 2, 3, 4, 5, 6, 7])
        :param iterables: iterable arguments
        :return: itself
        """
        _set = self._set
        _list = self._list
        values = set(chain(*iterables))
        if (4 * len(values)) > len(_set):
            _list = self._list
            _set.update(values)
            _list.clear()
            _list.update(_set)
        else:
            _add = self._add
            for value in values:
                _add(value)
        return self

    __ior__ = update
    _update = update

    def __reduce__(self):
        """Support for pickle.
        The tricks played with exposing methods in :func:`SortedSet.__init__`
        confuse pickle so customize the reducer.
        """
        return (type(self), (self._set, self._key))

    @recursive_repr()
    def __repr__(self):
        """Return string representation of sorted set.
        ``ss.__repr__()`` <==> ``repr(ss)``
        :return: string representation
        """
        _key = self._key
        key = '' if _key is None else ', key={0!r}'.format(_key)
        type_name = type(self).__name__
        return '{0}({1!r}{2})'.format(type_name, list(self), key)

    def _check(self):
        """Check invariants of sorted set.
        Runtime complexity: `O(n)`
        """
        _set = self._set
        _list = self._list
        _list._check()
        assert len(_set) == len(_list)
        assert all(value in _set for value in _list)


class SortedDict(dict):
    """Sorted dict is a sorted mutable mapping.
    Sorted dict keys are maintained in sorted order. The design of sorted dict
    is simple: sorted dict inherits from dict to store items and maintains a
    sorted list of keys.
    Sorted dict keys must be hashable and comparable. The hash and total
    ordering of keys must not change while they are stored in the sorted dict.
    Mutable mapping methods:
    * :func:`SortedDict.__getitem__` (inherited from dict)
    * :func:`SortedDict.__setitem__`
    * :func:`SortedDict.__delitem__`
    * :func:`SortedDict.__iter__`
    * :func:`SortedDict.__len__` (inherited from dict)
    Methods for adding items:
    * :func:`SortedDict.setdefault`
    * :func:`SortedDict.update`
    Methods for removing items:
    * :func:`SortedDict.clear`
    * :func:`SortedDict.pop`
    * :func:`SortedDict.popitem`
    Methods for looking up items:
    * :func:`SortedDict.__contains__` (inherited from dict)
    * :func:`SortedDict.get` (inherited from dict)
    * :func:`SortedDict.peekitem`
    Methods for views:
    * :func:`SortedDict.keys`
    * :func:`SortedDict.items`
    * :func:`SortedDict.values`
    Methods for miscellany:
    * :func:`SortedDict.copy`
    * :func:`SortedDict.fromkeys`
    * :func:`SortedDict.__reversed__`
    * :func:`SortedDict.__eq__` (inherited from dict)
    * :func:`SortedDict.__ne__` (inherited from dict)
    * :func:`SortedDict.__repr__`
    * :func:`SortedDict._check`
    Sorted list methods available (applies to keys):
    * :func:`SortedList.bisect_left`
    * :func:`SortedList.bisect_right`
    * :func:`SortedList.count`
    * :func:`SortedList.index`
    * :func:`SortedList.irange`
    * :func:`SortedList.islice`
    * :func:`SortedList._reset`
    Additional sorted list methods available, if key-function used:
    * :func:`SortedKeyList.bisect_key_left`
    * :func:`SortedKeyList.bisect_key_right`
    * :func:`SortedKeyList.irange_key`
    Sorted dicts may only be compared for equality and inequality.
    """

    def __init__(self, *args, **kwargs):
        """Initialize sorted dict instance.
        Optional key-function argument defines a callable that, like the `key`
        argument to the built-in `sorted` function, extracts a comparison key
        from each dictionary key. If no function is specified, the default
        compares the dictionary keys directly. The key-function argument must
        be provided as a positional argument and must come before all other
        arguments.
        Optional iterable argument provides an initial sequence of pairs to
        initialize the sorted dict. Each pair in the sequence defines the key
        and corresponding value. If a key is seen more than once, the last
        value associated with it is stored in the new sorted dict.
        Optional mapping argument provides an initial mapping of items to
        initialize the sorted dict.
        If keyword arguments are given, the keywords themselves, with their
        associated values, are added as items to the dictionary. If a key is
        specified both in the positional argument and as a keyword argument,
        the value associated with the keyword is stored in the
        sorted dict.
        Sorted dict keys must be hashable, per the requirement for Python's
        dictionaries. Keys (or the result of the key-function) must also be
        comparable, per the requirement for sorted lists.
        >>> d = {'alpha': 1, 'beta': 2}
        >>> SortedDict([('alpha', 1), ('beta', 2)]) == d
        True
        >>> SortedDict({'alpha': 1, 'beta': 2}) == d
        True
        >>> SortedDict(alpha=1, beta=2) == d
        True
        """
        if args and (args[0] is None or callable(args[0])):
            _key = self._key = args[0]
            args = args[1:]
        else:
            _key = self._key = None

        self._list = SortedList(key=_key)

        # Reaching through ``self._list`` repeatedly adds unnecessary overhead
        # so cache references to sorted list methods.

        _list = self._list
        self._list_add = _list.add
        self._list_clear = _list.clear
        self._list_iter = _list.__iter__
        self._list_reversed = _list.__reversed__
        self._list_pop = _list.pop
        self._list_remove = _list.remove
        self._list_update = _list.update

        # Expose some sorted list methods publicly.

        self.bisect_left = _list.bisect_left
        self.bisect = _list.bisect_right
        self.bisect_right = _list.bisect_right
        self.index = _list.index
        self.irange = _list.irange
        self.islice = _list.islice
        self._reset = _list._reset

        if _key is not None:
            self.bisect_key_left = _list.bisect_key_left
            self.bisect_key_right = _list.bisect_key_right
            self.bisect_key = _list.bisect_key
            self.irange_key = _list.irange_key

        self._update(*args, **kwargs)

    @property
    def key(self):
        """Function used to extract comparison key from keys.
        Sorted dict compares keys directly when the key function is none.
        """
        return self._key

    @property
    def iloc(self):
        """Cached reference of sorted keys view.
        Deprecated in version 2 of Sorted Containers. Use
        :func:`SortedDict.keys` instead.
        """
        # pylint: disable=attribute-defined-outside-init
        try:
            return self._iloc
        except AttributeError:
            warnings.warn(
                'sorted_dict.iloc is deprecated.'
                ' Use SortedDict.keys() instead.',
                DeprecationWarning,
                stacklevel=2,
            )
            _iloc = self._iloc = SortedKeysView(self)
            return _iloc

    def clear(self):
        """Remove all items from sorted dict.
        Runtime complexity: `O(n)`
        """
        dict.clear(self)
        self._list_clear()

    def __delitem__(self, key):
        """Remove item from sorted dict identified by `key`.
        ``sd.__delitem__(key)`` <==> ``del sd[key]``
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> del sd['b']
        >>> sd
        SortedDict({'a': 1, 'c': 3})
        >>> del sd['z']
        Traceback (most recent call last):
          ...
        KeyError: 'z'
        :param key: `key` for item lookup
        :raises KeyError: if key not found
        """
        dict.__delitem__(self, key)
        self._list_remove(key)

    def __iter__(self):
        """Return an iterator over the keys of the sorted dict.
        ``sd.__iter__()`` <==> ``iter(sd)``
        Iterating the sorted dict while adding or deleting items may raise a
        :exc:`RuntimeError` or fail to iterate over all keys.
        """
        return self._list_iter()

    def __reversed__(self):
        """Return a reverse iterator over the keys of the sorted dict.
        ``sd.__reversed__()`` <==> ``reversed(sd)``
        Iterating the sorted dict while adding or deleting items may raise a
        :exc:`RuntimeError` or fail to iterate over all keys.
        """
        return self._list_reversed()

    def __setitem__(self, key, value):
        """Store item in sorted dict with `key` and corresponding `value`.
        ``sd.__setitem__(key, value)`` <==> ``sd[key] = value``
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict()
        >>> sd['c'] = 3
        >>> sd['a'] = 1
        >>> sd['b'] = 2
        >>> sd
        SortedDict({'a': 1, 'b': 2, 'c': 3})
        :param key: key for item
        :param value: value for item
        """
        if key not in self:
            self._list_add(key)
        dict.__setitem__(self, key, value)

    _setitem = __setitem__

    def copy(self):
        """Return a shallow copy of the sorted dict.
        Runtime complexity: `O(n)`
        :return: new sorted dict
        """
        return self.__class__(self._key, self.items())

    __copy__ = copy

    @classmethod
    def fromkeys(cls, iterable, value=None):
        """Return a new sorted dict initailized from `iterable` and `value`.
        Items in the sorted dict have keys from `iterable` and values equal to
        `value`.
        Runtime complexity: `O(n*log(n))`
        :return: new sorted dict
        """
        return cls((key, value) for key in iterable)

    def keys(self):
        """Return new sorted keys view of the sorted dict's keys.
        See :class:`SortedKeysView` for details.
        :return: new sorted keys view
        """
        return SortedKeysView(self)

    def items(self):
        """Return new sorted items view of the sorted dict's items.
        See :class:`SortedItemsView` for details.
        :return: new sorted items view
        """
        return SortedItemsView(self)

    def values(self):
        """Return new sorted values view of the sorted dict's values.
        See :class:`SortedValuesView` for details.
        :return: new sorted values view
        """
        return SortedValuesView(self)

    # if sys.hexversion < 0x03000000:
    #	 def __make_raise_attributeerror(original, alternate):
    #		 # pylint: disable=no-self-argument
    #		 message = (
    #			 'SortedDict.{original}() is not implemented.'
    #			 ' Use SortedDict.{alternate}() instead.'
    #		 ).format(original=original, alternate=alternate)

    #		 def method(self):
    #			 # pylint: disable=missing-docstring,unused-argument
    #			 raise AttributeError(message)
    #		 method.__name__ = original  # pylint: disable=non-str-assignment-to-dunder-name
    #		 method.__doc__ = message
    #		 return property(method)

    #	 iteritems = __make_raise_attributeerror('iteritems', 'items')
    #	 iterkeys = __make_raise_attributeerror('iterkeys', 'keys')
    #	 itervalues = __make_raise_attributeerror('itervalues', 'values')
    #	 viewitems = __make_raise_attributeerror('viewitems', 'items')
    #	 viewkeys = __make_raise_attributeerror('viewkeys', 'keys')
    #	 viewvalues = __make_raise_attributeerror('viewvalues', 'values')

    class _NotGiven(object):
        # pylint: disable=too-few-public-methods
        def __repr__(self):
            return '<not-given>'

    __not_given = _NotGiven()

    def pop(self, key, default=__not_given):
        """Remove and return value for item identified by `key`.
        If the `key` is not found then return `default` if given. If `default`
        is not given then raise :exc:`KeyError`.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> sd.pop('c')
        3
        >>> sd.pop('z', 26)
        26
        >>> sd.pop('y')
        Traceback (most recent call last):
          ...
        KeyError: 'y'
        :param key: `key` for item
        :param default: `default` value if key not found (optional)
        :return: value for item
        :raises KeyError: if `key` not found and `default` not given
        """
        if key in self:
            self._list_remove(key)
            return dict.pop(self, key)
        else:
            if default is self.__not_given:
                raise KeyError(key)
            return default

    def popitem(self, index=-1):
        """Remove and return ``(key, value)`` pair at `index` from sorted dict.
        Optional argument `index` defaults to -1, the last item in the sorted
        dict. Specify ``index=0`` for the first item in the sorted dict.
        If the sorted dict is empty, raises :exc:`KeyError`.
        If the `index` is out of range, raises :exc:`IndexError`.
        Runtime complexity: `O(log(n))`
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> sd.popitem()
        ('c', 3)
        >>> sd.popitem(0)
        ('a', 1)
        >>> sd.popitem(100)
        Traceback (most recent call last):
          ...
        IndexError: list index out of range
        :param int index: `index` of item (default -1)
        :return: key and value pair
        :raises KeyError: if sorted dict is empty
        :raises IndexError: if `index` out of range
        """
        if not self:
            raise KeyError('popitem(): dictionary is empty')

        key = self._list_pop(index)
        value = dict.pop(self, key)
        return (key, value)

    def peekitem(self, index=-1):
        """Return ``(key, value)`` pair at `index` in sorted dict.
        Optional argument `index` defaults to -1, the last item in the sorted
        dict. Specify ``index=0`` for the first item in the sorted dict.
        Unlike :func:`SortedDict.popitem`, the sorted dict is not modified.
        If the `index` is out of range, raises :exc:`IndexError`.
        Runtime complexity: `O(log(n))`
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> sd.peekitem()
        ('c', 3)
        >>> sd.peekitem(0)
        ('a', 1)
        >>> sd.peekitem(100)
        Traceback (most recent call last):
          ...
        IndexError: list index out of range
        :param int index: index of item (default -1)
        :return: key and value pair
        :raises IndexError: if `index` out of range
        """
        key = self._list[index]
        return key, self[key]

    def setdefault(self, key, default=None):
        """Return value for item identified by `key` in sorted dict.
        If `key` is in the sorted dict then return its value. If `key` is not
        in the sorted dict then insert `key` with value `default` and return
        `default`.
        Optional argument `default` defaults to none.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict()
        >>> sd.setdefault('a', 1)
        1
        >>> sd.setdefault('a', 10)
        1
        >>> sd
        SortedDict({'a': 1})
        :param key: key for item
        :param default: value for item (default None)
        :return: value for item identified by `key`
        """
        if key in self:
            return self[key]
        dict.__setitem__(self, key, default)
        self._list_add(key)
        return default

    def update(self, *args, **kwargs):
        """Update sorted dict with items from `args` and `kwargs`.
        Overwrites existing items.
        Optional arguments `args` and `kwargs` may be a mapping, an iterable of
        pairs or keyword arguments. See :func:`SortedDict.__init__` for
        details.
        :param args: mapping or iterable of pairs
        :param kwargs: keyword arguments mapping
        """
        if not self:
            dict.update(self, *args, **kwargs)
            self._list_update(dict.__iter__(self))
            return

        if not kwargs and len(args) == 1 and isinstance(args[0], dict):
            pairs = args[0]
        else:
            pairs = dict(*args, **kwargs)

        if (10 * len(pairs)) > len(self):
            dict.update(self, pairs)
            self._list_clear()
            self._list_update(dict.__iter__(self))
        else:
            for key in pairs:
                self._setitem(key, pairs[key])

    _update = update

    def __reduce__(self):
        """Support for pickle.
        The tricks played with caching references in
        :func:`SortedDict.__init__` confuse pickle so customize the reducer.
        """
        items = dict.copy(self)
        return (type(self), (self._key, items))

    @recursive_repr()
    def __repr__(self):
        """Return string representation of sorted dict.
        ``sd.__repr__()`` <==> ``repr(sd)``
        :return: string representation
        """
        _key = self._key
        type_name = type(self).__name__
        key_arg = '' if _key is None else '{0!r}, '.format(_key)
        item_format = '{0!r}: {1!r}'.format
        items = ', '.join(item_format(key, self[key]) for key in self._list)
        return '{0}({1}{{{2}}})'.format(type_name, key_arg, items)

    def _check(self):
        """Check invariants of sorted dict.
        Runtime complexity: `O(n)`
        """
        _list = self._list
        _list._check()
        assert len(self) == len(_list)
        assert all(key in self for key in _list)


def _view_delitem(self, index):
    """Remove item at `index` from sorted dict.
    ``view.__delitem__(index)`` <==> ``del view[index]``
    Supports slicing.
    Runtime complexity: `O(log(n))` -- approximate.
    >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
    >>> view = sd.keys()
    >>> del view[0]
    >>> sd
    SortedDict({'b': 2, 'c': 3})
    >>> del view[-1]
    >>> sd
    SortedDict({'b': 2})
    >>> del view[:]
    >>> sd
    SortedDict({})
    :param index: integer or slice for indexing
    :raises IndexError: if index out of range
    """
    _mapping = self._mapping
    _list = _mapping._list
    dict_delitem = dict.__delitem__
    if isinstance(index, slice):
        keys = _list[index]
        del _list[index]
        for key in keys:
            dict_delitem(_mapping, key)
    else:
        key = _list.pop(index)
        dict_delitem(_mapping, key)


class SortedKeysView(KeysView, Sequence):
    """Sorted keys view is a dynamic view of the sorted dict's keys.
    When the sorted dict's keys change, the view reflects those changes.
    The keys view implements the set and sequence abstract base classes.
    """
    __slots__ = ()

    @classmethod
    def _from_iterable(cls, it):
        return SortedSet(it)

    def __getitem__(self, index):
        """Lookup key at `index` in sorted keys views.
        ``skv.__getitem__(index)`` <==> ``skv[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> skv = sd.keys()
        >>> skv[0]
        'a'
        >>> skv[-1]
        'c'
        >>> skv[:]
        ['a', 'b', 'c']
        >>> skv[100]
        Traceback (most recent call last):
          ...
        IndexError: list index out of range
        :param index: integer or slice for indexing
        :return: key or list of keys
        :raises IndexError: if index out of range
        """
        return self._mapping._list[index]

    __delitem__ = _view_delitem


class SortedItemsView(ItemsView, Sequence):
    """Sorted items view is a dynamic view of the sorted dict's items.
    When the sorted dict's items change, the view reflects those changes.
    The items view implements the set and sequence abstract base classes.
    """
    __slots__ = ()

    @classmethod
    def _from_iterable(cls, it):
        return SortedSet(it)

    def __getitem__(self, index):
        """Lookup item at `index` in sorted items view.
        ``siv.__getitem__(index)`` <==> ``siv[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> siv = sd.items()
        >>> siv[0]
        ('a', 1)
        >>> siv[-1]
        ('c', 3)
        >>> siv[:]
        [('a', 1), ('b', 2), ('c', 3)]
        >>> siv[100]
        Traceback (most recent call last):
          ...
        IndexError: list index out of range
        :param index: integer or slice for indexing
        :return: item or list of items
        :raises IndexError: if index out of range
        """
        _mapping = self._mapping
        _mapping_list = _mapping._list

        if isinstance(index, slice):
            keys = _mapping_list[index]
            return [(key, _mapping[key]) for key in keys]

        key = _mapping_list[index]
        return key, _mapping[key]

    __delitem__ = _view_delitem


class SortedValuesView(ValuesView, Sequence):
    """Sorted values view is a dynamic view of the sorted dict's values.
    When the sorted dict's values change, the view reflects those changes.
    The values view implements the sequence abstract base class.
    """
    __slots__ = ()

    def __getitem__(self, index):
        """Lookup value at `index` in sorted values view.
        ``siv.__getitem__(index)`` <==> ``siv[index]``
        Supports slicing.
        Runtime complexity: `O(log(n))` -- approximate.
        >>> sd = SortedDict({'a': 1, 'b': 2, 'c': 3})
        >>> svv = sd.values()
        >>> svv[0]
        1
        >>> svv[-1]
        3
        >>> svv[:]
        [1, 2, 3]
        >>> svv[100]
        Traceback (most recent call last):
          ...
        IndexError: list index out of range
        :param index: integer or slice for indexing
        :return: value or list of values
        :raises IndexError: if index out of range
        """
        _mapping = self._mapping
        _mapping_list = _mapping._list

        if isinstance(index, slice):
            keys = _mapping_list[index]
            return [_mapping[key] for key in keys]

        key = _mapping_list[index]
        return _mapping[key]

    __delitem__ = _view_delitem
# Copyright 2014 - 2019 Grant Jenks
# https://github.com/grantjenks/python-sortedcontainers

"""
sl=SortedList()
sl.update(not sorted list/str) / list(sl) / list(reversed(sl)) / sl *3
sl.add(x)
pop(ind) / remove(value) /del sl[1] / clear() 
x in sl / sl.count(x) / sl.index(x) / sl.bl|br / sl[1:4]|sl.islace(1,4)
NG
sl[2]=x   <- del[2] & sl.add(x)
sl.reverse() <- reversed(sl)
sl.append(x) <- sl.add(x)
"""
sl=SortedList()
for i in range(int(input())):
    x=-int(input())
    ind=sl.bisect_right(x)
    if len(sl)!=ind:
        sl.pop(ind)
    sl.add(x)
print(len(sl))

import sys
from bisect import bisect_left

input = sys.stdin.readline


def main():
    N = int(input())
    A = [0] * N
    for i in range(N):
        A[i] = int(input())

    colors = []
    for a in A:
        idx = bisect_left(colors, a)
        if idx == 0:
            colors.insert(0, a)
        else:
            colors[idx - 1] = a

    ans = len(colors)
    print(ans)


def __starting_point():
    main()

__starting_point()

import sys
input = sys.stdin.readline
from collections import deque

N = int(input())
A = [int(input()) for _ in range(N)]
INF = 10**18
arr = deque([])
for a in A:
    l, r = -1, len(arr)
    while (r-l) > 1:
        mid = (r+l)//2
        if arr[mid] < a:
            l = mid
        else:
            r = mid
    if l == -1:
        arr.appendleft(a)
    else:
        arr[l] = a
print(len(arr))

#「与えられた数列の、広義単調減少列の長さの最大値 L」
from bisect import bisect_left, bisect_right
n = int(input())
INF = 1010101010
dp = [INF] * n
a = [int(input()) for _ in range(n)]
a.reverse()
for i in a:
    dp[bisect_right(dp, i)] = i
print(bisect_left(dp, INF))

# Longest non-increasing subsequenceの長さを求める
import sys
input = sys.stdin.readline

def bisect_right_reverse(L, target):
    ok = len(L)
    ng = -1
    while ok - ng > 1:
        mid = (ok + ng) // 2
        if L[mid] < target:
            ok = mid
        else:
            ng = mid
    return ok

N = int(input())
A = [int(input()) for _ in range(N)]

dp = [-1]  # A[i] >= 0なので、A[0] > dp[0]となる。
for i in range(N):
    k = bisect_right_reverse(dp, A[i])
    if k == len(dp):
        dp.append(A[i])
    else:
        dp[k] = A[i]

print(len(dp))

n=int(input())
a=[-int(input()) for i in range(n)]
from bisect import bisect_right
l=[]
for aa in a:
  idx=bisect_right(l,aa)
  if idx==len(l):
    l.append(aa)
  else:
    l[idx]=aa
print(len(l))

from _bisect import bisect_right
N=int(input())
A=[int(input()) for _ in range(N)][::-1]

DP=[A[0]]
for i in range(1,N):
    num=A[i]
    if num>=DP[-1]:
        DP.append(num)
    else:
        index=bisect_right(DP,num)
        DP[index]=num
print(len(DP))

import sys
import bisect
def input(): return sys.stdin.readline().rstrip()


def main():
    n = int(input())
    A=[0]*n
    for i in range(1,n+1):
        A[-i]=int(input())
    inf=10**10    
    dp=[inf]*(n+1)
    dp[0]=0
    for AA in A:
        bis=bisect.bisect_right(dp, AA)
        dp[bis]=AA #n以下の個数を数える
    print(bisect.bisect_right(dp, inf-100)-1)


def __starting_point():
    main()
__starting_point()

from collections import deque
from bisect import bisect_left

n=int(input())
d=deque()
for i in range(n):
  a=int(input())
  b=bisect_left(d,a)
  if b==0:
    d.appendleft(a)
  else:
    d[b-1]=a 
print((len(d)))


n = int(input())
a = [(-1)*int(input()) for _ in range(n)]
dp = [10**18 for _ in range(n)]

import bisect
def lis(a):
	"""
	param a: list
	return: the length of Longest increasing subsequence
	(be careful that [list] seq is not the LIS, but its length is accurate LIS length.)
	"""
	seq = []
	tmp = 0
	for i in a:
		pos = bisect.bisect_right(seq, i)
		if tmp <= pos:
			seq.append(i)
			tmp += 1
		else:
			seq[pos] = i
	return tmp

print(lis(a))


from bisect import bisect_left, bisect_right
from collections import deque
n = int(input())
l = []
for _ in range(n):
    a = int(input())
    l.append(a)


li = deque([l[0]])
for j in range(1,n):
    ai = l[j]
    ind = bisect_left(li , ai)
    if ind == 0:
        li.appendleft(ai)
    else:
        li[ind-1] = ai
print((len(li)))


from collections import deque
from bisect import bisect_left

n = int(input())
a = [int(input()) for _ in range(n)]

dq = deque()
for e in a:
    i = bisect_left(dq, e)
    if i == 0:
        dq.appendleft(e)
    else:
        dq[i-1] = e

ans = len(dq)
print(ans)


import bisect
n = int(input())
colors = [-1]*(n+1)
for _ in range(n):
    a = int(input())
    idx = bisect.bisect_left(colors, a)-1
    colors[idx] = a
print((n-bisect.bisect_left(colors, 0)+1))


from bisect import bisect_left
N = int(input())
A = [int(input()) for _ in range(N)]
L = [-1] * N
for a in A:
    idx = bisect_left(L, a)
    L[idx-1] = a
print((N - L.count(-1)))


import bisect
# from collections import deque
# from collections import Counter
# from fractions import gcd # >=Python3.5 # lcm（最小公倍数） = (a*b)//gcd(a,b)
# from fractions import Fraction
# from math import gcd # <Python3.5
# from math import sqrt
 
n = int(input())
li = [int(input()) for i in range(n)]
li = [-x for x in li]

X = []
X.append(li[0])

for i in range(1,n):
    idx = bisect.bisect_right(X,li[i])
    if idx==len(X):
        X.append(li[i])
    else:
        X[idx] = li[i]

print(len(X))

from bisect import bisect_right

n = int(input())
a = [-int(input()) for _ in range(n)]

li = list()
for e in a:
    i = bisect_right(li, e)
    if i == len(li):
        li.append(e)
    else:
        li[i] = e

ans = len(li)
print(ans)


# AtCoder Beginner Contest 134 E - Sequence Decomposing

import sys

N = int(input())
now = int(sys.stdin.readline().strip())
ans = [now]

for n in range(N - 1):
    now = int(sys.stdin.readline().strip())

    if now > ans[0]:
        ans[0] = now
    elif ans[-1] >= now:
        ans.append(now)
    else:
        # 2部探査で行進位置を探る
        left = 0  # 挿入不可能
        right = len(ans) - 1  # 挿入可能

        while right - left > 1:
            mid = (right + left) // 2
            if now > ans[mid]:
                right = mid
            else:
                left = mid

        ans[right] = now

print((len(ans)))


from bisect import bisect_right

N = int(input())
A = [int(input()) for _ in range(N)]
A = A[::-1]

LIS = []

for ai in A:
    index = bisect_right(LIS,ai)
    if index >= len(LIS):
        LIS.append(ai)
    else:
        LIS[index] = ai

print(len(LIS))

import bisect
n=int(input())
a=[int(input()) for _ in range(n)][::-1]
dp=[]
for i in a:
  if bisect.bisect_right(dp,i)==len(dp):
    dp.append(i)
  else:
    dp[bisect.bisect_right(dp,i)]=i
print(len(dp))

from collections import deque
import bisect

N = int(input())
A = [int(input()) for _ in range(N)]

Q = deque([A[0]])
for a in A[1:]:
    if a <= Q[0]:
        Q.appendleft(a)
    else:
        idx = bisect.bisect_left(Q,a)
        Q[idx-1] = a

print(len(Q))

N = int(input())
A = [int(input()) for _ in range(N)]
def solve(A,n):
    import bisect
    dp = [float('inf')] * n
    for i in range(n-1,-1,-1):
        k = bisect.bisect_right(dp, A[i])
        dp[k] = A[i]
    ans = 0
    for a in dp:
        if a != float('inf'):
            ans += 1
        else:
            break
    return ans
print(solve(A,N))

from bisect import bisect, bisect_left

N = int(input())
dp = [1]*N

for _ in range(N):
  a = -int(input())
  n = bisect(dp, a)
  dp[n] = a

print(sum( 1 for i in dp if i <= 0 ))

#!/usr/bin/env python3
from collections import deque
from bisect import bisect_left

def main():
    N, *A = map(int, open(0))
    # 方針: 色ごとに最大値をリストで保持する
    # 新しく色を追加しないといけない時は持っている全ての数字より小さい時->先頭追加なのでdequeがいい
    # dequeをbisectで二分探索できるんだ...
    q = deque([])
    for i in range(N):
        d = bisect_left(q,A[i])
        if d == 0:
            q.appendleft(A[i])
        else:
            q[d-1] = A[i]
    print(len(q))

def __starting_point():
    main()
__starting_point()

#!/usr/bin python3
# -*- coding: utf-8 -*-

from bisect import bisect_left

N = int(input())
A = []
for _ in range(N):
    x = int(input())
    idx = bisect_left(A, x)
    if idx == 0:
        A.insert(idx, x)
    elif A[idx-1] == x:
        A.insert(idx, x)
    else:
        A[idx-1] = x
#    print(A,x)
print((len(A)))


from collections import deque
import bisect

N = int(input())
table = deque()
for n in range(N):
    a = int(input())
    if n == 0:
        table.append(a)
    else:
        if a <= table[0]:
            table.appendleft(a)
        else:
            table[bisect.bisect_left(table, a)-1] = a
            
print(len(table))

import sys
import bisect

input = lambda: sys.stdin.readline().rstrip()

N = int(input())
A = [int(input()) for _ in range(N)]
A = A[::-1]


def solve():

    LIS = [A[0]]
    for i in range(1, N):

        if A[i] >= LIS[-1]:
            LIS.append(A[i])
        else:
            LIS[bisect.bisect_right(LIS, A[i])] = A[i]

    ans = len(LIS)
    print(ans)


def __starting_point():
    solve()

__starting_point()

# YouTube解説の方法（deque使わない）
import sys
input = sys.stdin.readline

def bisect_right_reverse(L, target):
    ok = len(L)
    ng = -1
    while ok - ng > 1:
        mid = (ok + ng) // 2
        if L[mid] < target:
            ok = mid
        else:
            ng = mid
    return ok

N = int(input())
A = [int(input()) for _ in range(N)]

L = []
for i in range(N):
    k = bisect_right_reverse(L, A[i])
    if k == len(L):
        L.append(A[i])
    else:
        L[k] = A[i]

print(len(L))

from bisect import bisect_right


N, *A = list(map(int, open(0).read().split()))
A = [-a for a in A]
q = []
for a in A:
    idx = bisect_right(q, a)
    if idx == len(q):
        q.append(a)
    else:
        q[idx] = a
print((len(q)))


import sys
readline = sys.stdin.readline

N = int(readline())
ans = []
import bisect
for i in range(N):
  a = -int(readline())
  ind = bisect.bisect_right(ans, a)
  if ind == len(ans):
    ans.append(a)
  else:
    ans[ind] = a
    
print((len(ans)))


import bisect


def main():
    INF = 10 ** 10
    N = int(input())
    A = [int(input()) for _ in range(N)]
    A.reverse()
    B = [INF for _ in range(N)]
    for a in A:
        index = bisect.bisect_right(B, a)
        B[index] = a
    ans = 0
    for b in B:
        if b == INF:
            break
        ans += 1
    print(ans)


def __starting_point():
    main()
__starting_point()

import bisect
n = int(input())
colors = [-1]*(n+1)
for _ in range(n):
    a = int(input())
    idx = bisect.bisect_left(colors, a)-1
    colors[idx] = a
print((colors[::-1].index(-1)))


import bisect, copy, heapq, math
from math import inf
import sys
from collections import *
from functools import lru_cache
from itertools import accumulate, combinations, permutations, product
def input():
    return sys.stdin.readline()[:-1]
def ruiseki(lst):
    return [0]+list(accumulate(lst))
def celi(a,b):
    return -(-a//b)
sys.setrecursionlimit(5000000)
mod=pow(10,9)+7
al=[chr(ord('a') + i) for i in range(26)]
direction=[[1,0],[0,1],[-1,0],[0,-1]]

n=int(input())
a=[int(input()) for i in range(n)]

now=n
lst=[-1]*n

for i in range(n):
    tmp=bisect.bisect_left(lst,a[i])
    if tmp==now:
        lst[tmp-1]=a[i]
        now-=1
    else:
        lst[tmp-1]=a[i]
# print(lst)
print(n-now)

import bisect
n = int(input())
a = [int(input()) for i in range(n)][::-1]

color_num = [a[0]]
for i in range(1, n):
    if a[i] >= color_num[-1]:
        color_num.append(a[i])
    else:
        temp = bisect.bisect_right(color_num, a[i])
        color_num[temp] = a[i]


print(len(color_num))

import bisect
N= int(input())
dp = [-1]*N
cnt =0
for i in range(N):
    a = int(input())
    idx = bisect.bisect_left(dp,a)
    dp[idx-1]=a
print((len(dp[bisect.bisect_left(dp,0):])))


import sys
input = sys.stdin.readline
from bisect import bisect_left, bisect_right


def read():
    N = int(input().strip())
    A = [int(input().strip()) for _ in range(N)]
    return N, A


def solve(N, A, INF=10**9+1):
    # LIS
    # dp[i]: 長さiの最長増加部分列(A[i]<A[j])のうち、最も小さい数字
    dp = [INF for i in range(N)]
    for i in range(N):
        a = A[N-i-1]
        idx = bisect_right(dp, a)
        dp[idx] = a
    return bisect_right(dp, INF-1)


def __starting_point():
    inputs = read()
    outputs = solve(*inputs)
    if outputs is not None:
        print(("%s" % str(outputs)))

__starting_point()

from bisect import bisect
N = int(input())
dp = [1]*N
for _ in range(N):
  a=-int(input())
  n=bisect(dp,a)
  dp[n]=a
print(sum(1 for i in dp if i <= 0))

import bisect
from collections import deque

N = int(input())
A = []
for _ in range(N):
    A.append(int(input()))

buf = deque()
for a in A:
    i = bisect.bisect_right(buf, a-1)-1
    if i < 0:
        buf.appendleft(a)
    else:
        buf[i] = a
print(len(buf))

from collections import deque
from bisect import bisect_left

n = int(input())
a = [int(input()) for _ in range(n)]

dp = deque([])
b = []
for i in range(n):
    j = bisect_left(dp, a[i])
    if j == 0:
        dp.appendleft(a[i])
    else:
        dp[j-1] = a[i]

print((len(dp)))


from bisect import bisect_left
from collections import deque

n = int(input())
ans = []
ans.append(int(input()))
ans = deque(ans)
for i in range(n-1):  
  temp = int(input())
  if temp <= ans[0]:
    ans.appendleft(temp)
  else:
    ans[bisect_left(ans, temp)-1]=temp
print((len(ans)))


import bisect
N= int(input())
dp = [-1]*N
cnt =0
for _ in range(N):
    a = int(input())
    idx = bisect.bisect_left(dp,a)
    dp[idx-1]=a
print((len(dp[bisect.bisect_left(dp,0):])))


import bisect

n = int(input())
a = []

for i in range(n):
    x = int(input())
    a.append(x)
a = a[::-1]

stack = [a[0]]

for i in range(1, n):
    index = bisect.bisect_right(stack, a[i])
    if index == len(stack):
        stack.append(a[i])
    else:
        stack[index] = a[i]

print((len(stack)))


from bisect import bisect_left, bisect_right
N = int(input())
seq = [0] * N
for i in range(N):
  seq[i] = (-1) * int(input())

"""
#リストseqからLISの長さを出す
LIS = [seq[0]]
for i in range(len(seq)):
    if seq[i] >= LIS[-1]:
        LIS.append(seq[i])
    else:
        LIS[bisect_right(LIS, seq[i])] = seq[i]

print(len(LIS) - 1)
#print(LIS)
"""
def getLIS(_N,a):
  INF = 1e18
  MaxP = _N+10
  dp = [INF for _ in range(MaxP)]
  for i in range(_N):
    dp[bisect_right(dp,a[i])] = a[i]

  #print(dp[:10])
  return dp[:bisect_left(dp, INF)]

LIS2 = getLIS(N,seq)

print(len(LIS2))

from bisect import bisect_right

def main():
    inf=float("inf")
    n=int(input())
    alst=[int(input()) for _ in range(n)]
    work=[inf]*n
    for i in range(n-1,-1,-1):
        j=bisect_right(work,alst[i])
        work[j]=alst[i]
    print(n-work.count(inf))

main()

from bisect import bisect_right
n = int(input())

color = [0] * (n+1)
for i in range(n):
    a = int(input())
    j = bisect_right(color, a)
    color[j-1] = a+1

ans = 0
for t in color:
    if t != 0:
        ans += 1

print(ans)



from bisect import bisect_left,bisect_right
n=int(input())
INF=float('inf')
l=[INF]*n
a=[int(input()) for _ in range(n)]
for i in a[::-1]:
    l[bisect_right(l,i)]=i
print(bisect_left(l,INF))

import bisect

N = int(input())
A = [int(input()) for i in range(N)]

def lis(S):
    from bisect import bisect_right
    L = [S[0]]

    for s in S[1:]:
        if s >= L[-1]:
            L.append(s)
        else:
            L[bisect_right(L, s)] = s
    return len(L)

print(lis(A[::-1]))

import sys
from bisect import bisect_right

read = sys.stdin.read
readline = sys.stdin.readline
readlines = sys.stdin.readlines
sys.setrecursionlimit(10 ** 9)
INF = 1 << 60


def main():
    N, *A = list(map(int, read().split()))

    vec = [INF] * N
    for a in reversed(A):
        vec[bisect_right(vec, a)] = a

    try:
        print((vec.index(INF)))
    except:
        print(N)

    return


def __starting_point():
    main()

__starting_point()

from bisect import bisect

n=int(input())
a=[0]*n
for i in range(n):
    a[i]=-int(input())

def loopa():

    n=len(a)
    dp = [10**10]*(n+1)
    dp[0] = -10**10
    
    for i in range(n):
        idx = bisect(dp, a[i])
        dp[idx] = min(a[i], dp[idx])

    return dp

dp=loopa()        

print((bisect(dp, 10**10-1)-1))    



# 貪欲でいいよね。
# 最初から順に見ていって、増加列で取れるならそのうち最大のもので取る、無理なら新設する。
# これが最適な理由は……厳密には難しいなぁ。ちゃんと分かってない。

# でこれを実現するデータ構造は何が良いかを考えるわけよね。
# ソートしたデータから指定のものを探す・値を取り出す・新規で入れる をしなきゃいけないから、C++のset（平衡二分探索木）使わないと無理じゃね?
# と一瞬思うけど、普通のリストで可能。
# なぜなら、値を書き換えることでソートは崩れないから。
# Tが来たら、T未満のうち最も右の要素を見つけて、Tに書き換える。T未満とT以上の境界なので、Tに書き換えても値はソート順を飛び越さない。

# 昇順で管理だとTLEなので降順にします→bisectが使えないやんけ→-1倍して昇順にします

import bisect

n = int(input())

ans = 0
mylist = []
for i in range(n):
    k = int(input())
    idx = bisect.bisect(mylist, -k)
    if idx == ans:
        mylist.append(-k)
        ans += 1
    else:
        mylist[idx] = -k  # この操作によりソートは崩れない

print(ans)


#!/usr/bin/env python3
import sys
from bisect import bisect_right

INF = float("inf")


def main():
    N = int(input())
    A = [int(input()) for _ in range(N)]

    dp = [INF]*N
    for i in range(N):
        j = bisect_right(dp, -A[i])
        dp[j] = -A[i]

    print((sum([1 for d in dp if d < INF])))
    # print(dp)


def __starting_point():
    main()

__starting_point()

from collections import deque
from bisect import bisect_left
N = int(input())
A = [int(input()) for i in range(N)]

X = deque([])

for a in A:
  i = bisect_left(X,a)
  if i == 0:
    X.appendleft(a)
  else:
    X[i-1] = a
print(len(X))

from bisect import bisect_right
N = int(input())
A = [int(input()) for _ in range(N)]
ans = 1
min_vs = [A[-1]]
for i in range(N-2, -1, -1):
    a = A[i]
    idx = bisect_right(min_vs, a)
    if idx == len(min_vs):
        ans += 1
        min_vs.append(a)
    else:
        min_vs[idx] = a
print(ans)


def binary_search(key):
    bad, good = -1, len(ans)
    while good - bad > 1:
        mid = (bad + good) // 2
        if ans[mid][-1] < key:
            good = mid
        else:
            bad = mid
    return good


N = int(input())
ans = []
for _ in range(N):
    A = int(input())
    idx = binary_search(A)
    if idx == len(ans):
        ans.append([A])
    else:
        ans[idx].append(A)
print((len(ans)))


import bisect
from collections import deque

N = int(input())
A = [int(input()) for _ in range(N)]

dp = deque()
dp.append(A[0])

for i in range(1, N):
    ind = bisect.bisect_left(dp, A[i])

    if ind == 0:
        dp.appendleft(A[i])
    elif dp[-1] < A[i]:
        dp[-1] = A[i]
    else:
        dp[ind-1] = A[i]

print(len(dp))

from collections import deque
import bisect

N = int(input())

A = [int(input()) for i in range(N)]

q = deque([A[0]])

for a in A[1:]:
    idx = bisect.bisect_left(q, a)
    if idx == 0:
        q.appendleft(a)
    else:
        q[idx-1] = a

print(len(q))

from bisect import bisect_left
from collections import deque

n = int(input())
A = [int(input()) for _ in range(n)]

D = deque([])

for a in A:
    i = bisect_left(D, a)
    if i == 0:
        D.appendleft(a)
    else:
        D[i-1] = a

print(len(D))

import bisect
from collections import deque

N = int(input())
A = [int(input()) for _ in range(N)]

dp = deque()
dp.append(A[0])

for i in range(1, N):
    ind = bisect.bisect_left(dp, A[i])

    if ind == 0:
        dp.appendleft(A[i])
    # elif dp[-1] < A[i]:
    #     dp[-1] = A[i]
    else:
        dp[ind-1] = A[i]

print(len(dp))

from collections import deque
from bisect import bisect_left
N = int(input())
A = [int(input()) for _ in range(N)]
que = deque()
for i in range(N):
    k = bisect_left(que, A[i])
    if k == 0:
        que.appendleft(A[i])
    else:
        que[k - 1] = A[i]

print(len(que))

import bisect
n, *a = list(map(int, open(0).read().split()))
a = a[::-1]
INF = 1 << 30
dp = [INF] * (n + 1)

for e in a:
    dp[bisect.bisect_right(dp, e)] = e
print((bisect.bisect_left(dp, INF)))


import sys
from bisect import bisect_right

read = sys.stdin.read
readline = sys.stdin.readline
readlines = sys.stdin.readlines
sys.setrecursionlimit(10 ** 9)
INF = 1 << 60
MOD = 1000000007


def main():
    N, *A = list(map(int, read().split()))

    vec = [A[-1]]
    for a in reversed(A[:-1]):
        if a >= vec[-1]:
            vec.append(a)
        else:
            vec[bisect_right(vec, a)] = a

    print((len(vec)))
    return


def __starting_point():
    main()

__starting_point()

import sys
from collections import deque
from bisect import bisect_left

sys.setrecursionlimit(10 ** 7)
input = sys.stdin.readline
f_inf = float('inf')
mod = 10 ** 9 + 7


def resolve():
    n = int(input())
    A = list(int(input()) for _ in range(n))

    que = deque([A[0]])
    for i in range(1, n):
        idx = bisect_left(que, A[i])
        if idx == 0:
            que.appendleft(A[i])
        else:
            idx -= 1
            que[idx] = A[i]
    print((len(que)))


def __starting_point():
    resolve()

__starting_point()

from bisect import bisect_left
n = int(input())
a = [int(input()) for i in range(n)]
dp = [-float("inf")] * (n + 1)
for i in a:
    dp[bisect_left(dp, i) - 1] = i
print(dp[::-1].index(-float("inf")))

def bisect_right_reverse(a, x):
    '''
    reverseにソートされたlist aに対してxを挿入できるidxを返す。
    xが存在する場合には一番右側のidx+1となる。
    '''
    if a[0] < x:
        return 0
    if x <= a[-1]:
        return len(a)
    # 二分探索
    ok = len(a) - 1
    ng = 0
    while (abs(ok - ng) > 1):
        mid = (ok + ng) // 2
        if a[mid] < x:
            ok = mid
        else:
            ng = mid
    return ok

def main():
  N = int(input())
  ans = 1
  lis_min = [int(input())]
  for i in range(N-1):
    a = int(input())
    index = bisect_right_reverse(lis_min, a)
    if index != len(lis_min):
      lis_min[index] = a
    else:
      ans += 1
      lis_min.append(a)
  print(ans)
def __starting_point():
  main()
__starting_point()

import sys
from bisect import bisect_right

read = sys.stdin.read
readline = sys.stdin.readline
readlines = sys.stdin.readlines
sys.setrecursionlimit(10 ** 9)
INF = 1 << 60


def main():
    N, *A = list(map(int, read().split()))

    vec = [A[-1]]
    for a in reversed(A[:-1]):
        if a >= vec[-1]:
            vec.append(a)
        else:
            vec[bisect_right(vec, a)] = a

    print((len(vec)))
    return


def __starting_point():
    main()

__starting_point()

#import sys
#import numpy as np
import math
#from fractions import Fraction
import itertools
from collections import deque
from collections import Counter
import heapq
#from fractions  import gcd
#input=sys.stdin.readline
import bisect
n=int(input())
d=[]
a=(int(input()))*-1
d.append(a)
ans=1
for i in range(n-1):
    a=-1*(int(input()))
    ind=bisect.bisect_right(d,a)
    l=len(d)
    if ind==l:
        d.append(a)
        ans+=1
    else:
        d[ind]=a
print(ans)

import math,itertools,fractions,heapq,collections,bisect,sys,queue,copy

sys.setrecursionlimit(10**7)
inf=10**20
mod=10**9+7
dd=[(-1,0),(0,1),(1,0),(0,-1)]
ddn=[(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1)]

def LI(): return [int(x) for x in sys.stdin.readline().split()]
# def LF(): return [float(x) for x in sys.stdin.readline().split()]
def I(): return int(sys.stdin.readline())
def F(): return float(sys.stdin.readline())
def LS(): return sys.stdin.readline().split()
def S(): return input()

def main():
  n=I()
  _l=[I() for _ in range(n)]
  l=collections.deque()

  for A in _l:
    if len(l)==0:
      l.append(A)
      continue

    lv=l[0]
    rv=l[-1]
    if rv<A:
      l[-1]=A
      continue
    if A<=lv:
      l.appendleft(A)
      continue

    li=0
    ri=len(l)-1
    while ri-li>1:
      mi=(ri+li)//2
      if l[mi]<A:
        li=mi
      else:
        ri=mi

    l[li]=A

  # print(l)
  return len(l)

# main()
print((main()))


import sys
def input(): return sys.stdin.readline().strip()
def mapint(): return map(int, input().split())
sys.setrecursionlimit(10**9)

N = int(input())
As = [-int(input()) for _ in range(N)]

from bisect import bisect_right
lis = [1]
for a in As:
    if a>=lis[-1]:
        lis.append(a)
    else:
        lis[bisect_right(lis, a)] = a

print(len(lis))

import bisect
from collections import deque

n = int(input())
#c = []
c = deque([])
for i in range(n):
    a = int(input())
    if i == 0:
        c.append(a)
    else:
        ind = bisect.bisect_left(c,a)
        if ind > 0:
            c[ind-1] = a
        else:
            c.appendleft(a)

print(len(c))

def resolve():
    N = int(input())
    A = [int(input()) for _ in range(N)]
    import bisect
    import collections
    colormax = collections.deque([A[0]])
    for i in range(1, N):
        idx = bisect.bisect_left(colormax, A[i])
        if idx == 0:
            colormax.appendleft(A[i])
        else:
            colormax[idx-1] = A[i]
    print(len(colormax))



if '__main__' == __name__:
    resolve()

#!/usr/bin/env python3
import sys
from bisect import bisect_left
from bisect import bisect_right

INF = float("inf")


def main():
    N = int(input())
    A = [int(input()) for _ in range(N)]

    dp = [INF]*N
    for i in range(N):
        j = bisect_right(dp, -A[i])
        dp[j] = -A[i]

    print((bisect_left(dp, INF)))


def __starting_point():
    main()

__starting_point()

from random import random
from sys import setrecursionlimit

setrecursionlimit(10 ** 6)


class TreapNode:
    _value = None
    _priority = None
    _count = None
    _left = None
    _right = None

    def __init__(self, value):
        self._value = value
        self._priority = random()
        self._count = 1


def treap_rotate_right(n):
    l = n._left
    n._left = l._right
    l._right = n
    return l


def treap_rotate_left(n):
    r = n._right
    n._right = r._left
    r._left = n
    return r


def treap_insert(n, v):
    if n is None:
        return TreapNode(v)
    if n._value == v:
        n._count += 1
        return n
    if n._value > v:
        n._left = treap_insert(n._left, v)
        if n._priority > n._left._priority:
            n = treap_rotate_right(n)
    else:
        n._right = treap_insert(n._right, v)
        if n._priority > n._right._priority:
            n = treap_rotate_left(n)
    return n


def treap_delete(n, v):
    if n is None:
        raise Exception('no nodes')
    if n._value > v:
        n._left = treap_delete(n._left, v)
        return n
    if n._value < v:
        n._right = treap_delete(n._right, v)
        return n

    # n._value == v
    if n._count > 1:
        n._count -= 1
        return n

    if n._left is None and n._right is None:
        return None

    if n._left is None:
        n = treap_rotate_left(n)
    elif n._right is None:
        n = treap_rotate_right(n)
    else:
        # n._left is not None and n._right is not None
        if n._left._priority < n._right._priority:
            n = treap_rotate_right(n)
        else:
            n = treap_rotate_left(n)
    return treap_delete(n, v)


def treap_size(n):
    if n is None:
        return 0
    return n._count + treap_size(n._left) + treap_size(n._right)


def treap_str(n):
    if n is None:
        return ""
    result = []
    if n._left is not None:
        result.append(treap_str(n._left))
    result.append("%d:%d" % (n._value, n._count))
    if n._right is not None:
        result.append(treap_str(n._right))
    return ' '.join(result)


def treap_search(n, v):
    # v 未満で最大のノードを検索する. v 未満のノードがなければ None を返す
    if n is None:
        return None
    if n._value >= v:
        if n._left is None:
            return None
        return treap_search(n._left, v)
    # n._value < v
    if n._right is None:
        return n
    r = treap_search(n._right, v)
    if r is None:
        return n
    return r


class Treap:
    _root = None
    _size = 0

    def insert(self, v):
        self._root = treap_insert(self._root, v)
        self._size += 1

    def delete(self, v):
        self._root = treap_delete(self._root, v)
        self._size -= 1

    def __len__(self):
        return self._size

    def __str__(self):
        return treap_str(self._root)

    def search(self, v):
        return treap_search(self._root, v)


N = int(input())
A = [int(input()) for _ in range(N)]

t = Treap()
for a in A:
    n = t.search(a)
    if n is not None:
        t.delete(n._value)
    t.insert(a)
print((len(t)))


from collections import deque
from bisect import bisect_left

n=int(input())
a=[int(input()) for i in range(n)]

d=deque()
for i in range(n):
  b=bisect_left(d,a[i])
  if b==0:
    d.appendleft(a[i])
  else:
    d[b-1]=a[i]
    
print((len(d)))


import sys
from collections import deque
from bisect import bisect_left

sys.setrecursionlimit(10 ** 7)
input = sys.stdin.readline
f_inf = float('inf')
mod = 10 ** 9 + 7


def resolve():
    n = int(input())
    A = list(int(input()) for _ in range(n))

    LIS = deque()
    for i in range(n):
        if len(LIS) == 0:
            LIS.appendleft(A[i])
        else:
            idx = bisect_left(LIS, A[i])
            if idx == 0:
                LIS.appendleft(A[i])
            else:
                LIS[idx - 1] = A[i]
    print((len(LIS)))


def __starting_point():
    resolve()

__starting_point()

import bisect

N = int(input())
A = [int(input()) for _ in range(N)]
dp = [-1] * N
dp[N-1] = A[0]

ans = 0
for i in range(1, N):
    target_index = bisect.bisect_left(dp, A[i])
    dp[target_index-1] = A[i]

print(N - dp.count(-1))

N = int(input())
A = list(int(input()) for _ in range(N))

S = [A[0]]
for i in range(1, N):
    if A[i] <= S[0]:
        S.insert(0, A[i])
        continue
    ok = 0
    ng = len(S)
    tmp = (ok+ng)
    while ok+1 < ng:
        tmp = (ok+ng)//2
        if A[i] > S[tmp]:
            ok = tmp
        else:
            ng = tmp
    S[ok] = A[i]
print((len(S)))


import sys
from bisect import bisect_right

read = sys.stdin.read
readline = sys.stdin.readline
readlines = sys.stdin.readlines
sys.setrecursionlimit(10 ** 9)
INF = 1 << 60


def main():
    N, *A = list(map(int, read().split()))

    vec = [INF] * N
    for a in reversed(A):
        vec[bisect_right(vec, a)] = a

    print((N - vec.count(INF)))
    return


def __starting_point():
    main()

__starting_point()

N = int(input())
A = []
for i in range(N):
  temp = int(input())
  A.append(temp)
#print(A)
L = [A[0]]
for i in range(1,N):
  if L[-1] >= A[i]:
    L.append(A[i])
  else:
    if L[0] < A[i]:
      L[0] = A[i]
    else:
      s = len(L)
      ok = s
      ng = 0
      while abs(ok-ng)>1:
        mid = (ok+ng)//2
        if L[mid] < A[i]:
          ok = mid
        else:
          ng = mid
      L[ok] = A[i]
  #print(L)
ans = len(L)
print(ans)

#float型を許すな
#numpyはpythonで
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**7)
from collections import Counter, deque
from collections import defaultdict
from itertools import combinations, permutations, accumulate, groupby, product
from bisect import bisect_left,bisect_right
from heapq import heapify, heappop, heappush
from math import floor, ceil,pi,factorial
from operator import itemgetter
def I(): return int(input())
def MI(): return list(map(int, input().split()))
def LI(): return list(map(int, input().split()))
def LI2(): return [int(input()) for i in range(n)]
def MXI(): return [[LI()]for i in range(n)]
def SI(): return input().rstrip()
def printns(x): print(('\n'.join(x)))
def printni(x): print(('\n'.join(list(map(str,x)))))
inf = 10**17
mod = 10**9 + 7
n=I()
lis=[I() for i in range(n)]
cols=deque([inf])
i=0
while i<n:
    #print(cols)
    x=lis[i]
    if cols[0]>=x:
        cols.appendleft(x)
    else:
        cols[bisect_left(cols,x)-1]=x
        #print(bisect_left(cols,x))
    i+=1
#print(cols)
print((len(cols)-1))

    
    



import bisect

N = int(input())
A = [int(input()) for _ in range(N)]

A.reverse()

dp = [A[0]]

for a in A[1:]:
    idx = bisect.bisect_right(dp, a)
    if idx == len(dp):
        dp.append(a)
    else:
        dp[idx] = a

print(len(dp))

from bisect import *
def solve():
    ans = 0
    N = int(input())
    A = [-int(input()) for _ in range(N)]
    lis = [A[0]]
    for i in range(1,N):
        if A[i]>=lis[-1]:
            lis.append(A[i])
        else:
            ind = bisect_right(lis,A[i])
            lis[ind] = A[i]
    ans = len(lis)
    return ans
print(solve())

from bisect import bisect
import heapq

N = int(input())
A = [int(input()) for _ in range(N)]
q = []
A.reverse()

for i in range(N):
  ii = bisect(q,A[i])
  if ii == len(q):
    heapq.heappush(q,A[i])
  else:
    q[ii] = A[i]
    
print(len(q))

from collections import deque
import bisect
N=int(input())
L=deque()
for i in range(N):
  s=int(input())
  if i==0:
    L.append(s)
  else:
    if s<=L[0]:
      L.appendleft(s)
    else:
      L[bisect.bisect(L,s-1)-1]=s
print(len(L))

import sys
import bisect
def input(): return sys.stdin.readline().rstrip()
 
 
def main():
    n = int(input())
    A=[0]*n
    for i in range(1,n+1):
        A[-i]=int(input())
    inf=10**10    
    dp=[0]
    for AA in A:
        if dp[-1]<=AA:
            dp.append(AA)
        else:
            bis=bisect.bisect_right(dp, AA)
            dp[bis]=AA #n以下の個数を数える
    print(len(dp)-1)
 
 
def __starting_point():
    main()
__starting_point()

import sys
from bisect import bisect_left, insort_right

input = sys.stdin.readline


def main():
    N = int(input())
    A = [0] * N
    for i in range(N):
        A[i] = int(input())

    colors = []
    for a in A:
        idx = bisect_left(colors, a)
        if idx != 0:
            colors.pop(idx - 1)
        insort_right(colors, a)

    ans = len(colors)
    print(ans)


def __starting_point():
    main()

__starting_point()

import sys
from bisect import bisect_right

input = sys.stdin.readline


def main():
    N = int(input())
    A = [0] * N
    for i in range(N):
        A[i] = int(input())

    A = [-a for a in A]

    colors = []
    for a in A:
        idx = bisect_right(colors, a)
        if idx == len(colors):
            colors.append(a)
        else:
            colors[idx] = a

    ans = len(colors)
    print(ans)


def __starting_point():
    main()

__starting_point()

n=int(input())
a=[int(input())for _ in range(n)]
dp=[2]*(n+1)
from bisect import bisect_left,bisect_right
for i in a:
  dp[bisect_left(dp,-i+1)]=-i
print(dp.index(2))

from bisect import bisect_right

n = int(input())
a = [-int(input()) for _ in range(n)]

lst = [a[0]]
for aa in a[1:]:
    i = bisect_right(lst, aa)
    if i == len(lst): lst.append(aa)
    else: lst[i] = aa

print(len(lst))

from random import random
from sys import setrecursionlimit

setrecursionlimit(10 ** 5)


class TreapNode:
    _value = None
    _priority = None
    _count = None
    _left = None
    _right = None

    def __init__(self, value):
        self._value = value
        self._priority = random()
        self._count = 1


def treap_rotate_right(n):
    l = n._left
    n._left = l._right
    l._right = n
    return l


def treap_rotate_left(n):
    r = n._right
    n._right = r._left
    r._left = n
    return r


def treap_insert(n, v):
    if n is None:
        return TreapNode(v)
    if n._value == v:
        n._count += 1
        return n
    if n._value > v:
        n._left = treap_insert(n._left, v)
        if n._priority > n._left._priority:
            n = treap_rotate_right(n)
    else:
        n._right = treap_insert(n._right, v)
        if n._priority > n._right._priority:
            n = treap_rotate_left(n)
    return n


def treap_delete(n, v):
    if n is None:
        raise Exception('no nodes')
    if n._value > v:
        n._left = treap_delete(n._left, v)
        return n
    if n._value < v:
        n._right = treap_delete(n._right, v)
        return n

    # n._value == v
    if n._count > 1:
        n._count -= 1
        return n

    if n._left is None and n._right is None:
        return None

    if n._left is None:
        n = treap_rotate_left(n)
    elif n._right is None:
        n = treap_rotate_right(n)
    else:
        # n._left is not None and n._right is not None
        if n._left._priority < n._right._priority:
            n = treap_rotate_right(n)
        else:
            n = treap_rotate_left(n)
    return treap_delete(n, v)


def treap_size(n):
    if n is None:
        return 0
    return n._count + treap_size(n._left) + treap_size(n._right)


def treap_str(n):
    if n is None:
        return ""
    result = []
    if n._left is not None:
        result.append(treap_str(n._left))
    result.append("%d:%d" % (n._value, n._count))
    if n._right is not None:
        result.append(treap_str(n._right))
    return ' '.join(result)


def treap_search(n, v):
    # v 未満で最大のノードを検索する. v 未満のノードがなければ None を返す
    if n is None:
        return None
    if n._value >= v:
        if n._left is None:
            return None
        return treap_search(n._left, v)
    # n._value < v
    if n._right is None:
        return n
    r = treap_search(n._right, v)
    if r is None:
        return n
    return r


class Treap:
    _root = None
    _size = 0

    def insert(self, v):
        self._root = treap_insert(self._root, v)
        self._size += 1

    def delete(self, v):
        self._root = treap_delete(self._root, v)
        self._size -= 1

    def __len__(self):
        return self._size

    def __str__(self):
        return treap_str(self._root)

    def search(self, v):
        return treap_search(self._root, v)


N = int(input())
A = [int(input()) for _ in range(N)]

t = Treap()
for a in A:
    n = t.search(a)
    if n is not None:
        t.delete(n._value)
    t.insert(a)
print((len(t)))


import heapq
import bisect
N=int(input())
A=[]

for i in range(N):
  a=int(input())
  A.append(a)
h=[]
c=0
for i in range(N-1,-1,-1):
  p=bisect.bisect_right(h,A[i])
  if p==len(h):
    h.append(A[i])
    c+=1
  else:
    h[p]=A[i]
print(c)

from bisect import bisect_right, insort_right
N = int(input())
A = [int(input()) for _ in range(N)][::-1]
res = [A[0]]
for a in A[1:]:
    idx = bisect_right(res, a)
    if idx == len(res):
        res.append(a)
    else:
        if res[idx] == a:
            insort_right(res, a)   
        else:
            res[idx] = a
print((len(res)))