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

#ifdef LOCAL
#define DEBUG(...) debug(#__VA_ARGS__, __VA_ARGS__)
#else
#define DEBUG(...) 6
#endif

template<typename T, typename S> ostream& operator << (ostream &os, const pair<T, S> &p) {return os << "(" << p.first << ", " << p.second << ")";}
template<typename C, typename T = decay<decltype(*begin(declval<C>()))>, typename enable_if<!is_same<C, string>::value>::type* = nullptr>
ostream& operator << (ostream &os, const C &c) {bool f = true; os << "["; for (const auto &x : c) {if (!f) os << ", "; f = false; os << x;} return os << "]";}
template<typename T> void debug(string s, T x) {cerr << "\033[1;35m" << s << "\033[0;32m = \033[33m" << x << "\033[0m\n";}
template<typename T, typename... Args> void debug(string s, T x, Args... args) {for (int i=0, b=0; i<(int)s.size(); i++) if (s[i] == '(' || s[i] == '{') b++; else
if (s[i] == ')' || s[i] == '}') b--; else if (s[i] == ',' && b == 0) {cerr << "\033[1;35m" << s.substr(0, i) << "\033[0;32m = \033[33m" << x << "\033[31m | "; debug(s.substr(s.find_first_not_of(' ', i + 1)), args...); break;}}

#include <ext/pb_ds/assoc_container.hpp>
using namespace __gnu_pbds;

using ordered_set = tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update>;

// https://c...content-available-to-author-only...s.com/blog/entry/95575

#define len(a) (ll)a.size()
typedef long long ll;
typedef long double ld;

bool test=false;
ll mod1=1e9+7;
ll mod2=998244353;
ll inf=1e10+5;

enum colour {red, black};

struct Node {

    ll data, size;
    bool colour;
    Node *parent, *left, *right;
};

class RBTree {
public: // added this to gain access to tNil

    Node *root, *tNil;

    Node* findNode(Node *z) {

        Node *x=this->root;

        while(x!=tNil) {

            if(x->data==z->data) {

                return x;
            } else if(z->data<x->data) {

                x=x->left;
            } else {

                x=x->right;
            }
        }

        return tNil;
    }

    Node* findMin(Node *z) {

        while(z->left!=tNil) {

            z=z->left;
        }

        return z;
    }

    void leftRotate(Node *x) {

        if(x->right==tNil) {

            return;
        }

        Node *y=x->right;
        x->right=y->left;

        if(y->left!=tNil) {

            y->left->parent=x;
        }

        y->parent=x->parent;

        if(x->parent==tNil) {

            this->root=y;
        } else if(x==x->parent->left) {

            x->parent->left=y;
        } else {

            x->parent->right=y;
        }

        y->left=x;
        x->parent=y;
        y->size=x->size;
        x->size=x->left->size+x->right->size+1;
    }

    void rightRotate(Node *y) {

        if(y->left==tNil) {

            return;
        }

        Node *x=y->left;
        y->left=x->right;

        if(x->right!=tNil) {

            x->right->parent=y;
        }

        x->parent=y->parent;

        if(y->parent==tNil) {

            this->root=x;
        } else if(y==y->parent->right) {

            y->parent->right=x;
        } else {

            y->parent->left=x;
        }

        x->right=y;
        y->parent=x;
        x->size=y->size;
        y->size=y->left->size+y->right->size+1;
    }

    void insertFixUp(Node *z) {

        while(z->parent->colour==red) {

            if(z->parent==z->parent->parent->left) {

                Node *y=z->parent->parent->right;

                if(y->colour==red) {

                    z->parent->colour=black;
                    y->colour=black;

                    if(z->parent->parent!=this->root) {

                        z->parent->parent->colour=red;
                    }
                    z=z->parent->parent;
                } else {

                    if(z==z->parent->right) {

                        z=z->parent;
                        leftRotate(z);
                    }

                    z->parent->colour=black;
                    z->parent->parent->colour=red;
                    rightRotate(z->parent->parent);
                }
            } else {

                Node *y=z->parent->parent->left;

                if(y->colour==red) {

                    z->parent->colour=black;
                    y->colour=black;

                    if(z->parent->parent!=this->root) {

                        z->parent->parent->colour=red;
                    }
                    z=z->parent->parent;
                } else {

                    if(z==z->parent->left) {

                        z=z->parent;
                        rightRotate(z);
                    }

                    z->parent->colour=black;
                    z->parent->parent->colour=red;
                    leftRotate(z->parent->parent);
                }
            }
        }
    }

    void insertNode(Node *z) {

        if(this->root==tNil) {

            z->colour=black;
            this->root=z;
            this->root->size=1;
            return;
        }

        Node *y=tNil, *x=this->root;

        while(x!=tNil) {

            y=x;
            x->size++;

            if(z->data<x->data) {

                x=x->left;
            } else {

                x=x->right;
            }
        }

        z->parent=y;

        if(z->data<y->data) {

            y->left=z;
        } else {

            y->right=z;
        }

        insertFixUp(z);
    }

    void transplant(Node *u, Node *v) {

        if(u->parent==tNil) {

            this->root=v;
        } else if(u==u->parent->left) {

            u->parent->left=v;
        } else {

            u->parent->right=v;
        }
        v->parent=u->parent;
    }

    void deleteFixUp(Node *x) {

        while(x!=this->root && x->colour==black) {

            if(x==x->parent->left) {

                Node *w=x->parent->right;

                if(w->colour==red) {

                    w->colour=black;
                    x->parent->colour=red;
                    leftRotate(x->parent);

                    w=x->parent->right;
                }

                if(w->left->colour==black && w->right->colour==black) {

                    w->colour=red;
                    x=x->parent;
                } else {

                    if(w->right->colour==black) {

                        w->left->colour=black;
                        w->colour=red;
                        rightRotate(w);

                        w=x->parent->right;
                    }

                    w->colour=x->parent->colour;
                    x->parent->colour=red;
                    w->right->colour=black;
                    leftRotate(x->parent);
                    x=this->root;
                }
            } else {

                Node *w=x->parent->left;

                if(w->colour==red) {

                    w->colour=black;
                    x->parent->colour=red;
                    rightRotate(x->parent);

                    w=x->parent->left;
                }

                if(w->right->colour==black && w->left->colour==black) {

                    w->colour=red;
                    x=x->parent;
                } else {

                    if(w->left->colour==black) {

                        w->right->colour=black;
                        w->colour=red;
                        leftRotate(w);

                        w=x->parent->left;
                    }

                    w->colour=x->parent->colour;
                    x->parent->colour=red;
                    w->left->colour=black;
                    rightRotate(x->parent);
                    x=this->root;
                }
            }
        }
        x->colour=black;
    }

    void deleteNode(Node *z) {

        Node *y=z, *x;
        bool originalColour=y->colour;

        if(z->left==tNil) {

            x=z->right;
            transplant(z, z->right);
        } else if(z->right==tNil) {

            x=z->left;
            transplant(z, z->left);
        } else {

            y=findMin(z->right);
            originalColour=y->colour;

            x=y->right;

            if(y->parent==z) {

                x->parent=y;
            } else {

                transplant(y, y->right);
                y->right=z->right;
                y->right->parent=y;

                Node *s=x->parent;

                while(s!=tNil && s!=y) {

                    s->size--;
                    s=s->parent;
                }
            }

            transplant(z, y);

            y->left=z->left;
            y->left->parent=y;
            y->colour=z->colour;

            y->size=y->left->size+y->right->size+1;
        }

        if(originalColour==black) {

            deleteFixUp(x);
        }
    }

    void inOrderHelper(Node *node) {

        if(node==tNil) {

            return;
        }

        inOrderHelper(node->left);

        std::cout<<node->data<<" ";

        inOrderHelper(node->right);
    }

public:

    RBTree() {

        tNil=new Node();
        tNil->colour=black;
        tNil->size=0;

        tNil->left=tNil;
        tNil->right=tNil;
        tNil->parent=tNil;

        this->root=tNil;
    }

    Node* getRoot() {

        return this->root;
    }

    Node* find(ll key) {

        Node *z=new Node();

        z->data=key;

        return findNode(z);
    }

    void insert(ll key) {

        Node *z=new Node();
        z->data=key;
        z->colour=red;
        z->size=1;

        z->left=tNil;
        z->right=tNil;
        z->parent=tNil;

        insertNode(z);
    }

    void erase(ll key) {

        Node *z=find(key);

        if(z==tNil) {

            return;
        }

        Node *s=z->parent;

        while(s!=tNil) {

            s->size--;
            s=s->parent;
        }

        deleteNode(z);
    }

    ll osSelect(Node *x, ll i) {

        ll r=x->left->size+1;

        if(i==r) {

            return x->data;
        } else if(i<r) {

            return osSelect(x->left, i);
        } else {

            return osSelect(x->right, i-r);
        }
    }

    ll osRank(Node *x) {

        ll r=x->left->size+1;

        Node *y=x;

        while(y!=this->root) {

            if(y==y->parent->right) {

                r+=y->parent->left->size+1;
            }
            y=y->parent;
        }

        return r;
    }

    void inOrder() {

        inOrderHelper(this->root);
    }
};

const int N = 1e6;
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
vector<int> input;
clock_t start;
ordered_set st;
RBTree rbt;

void testInsert() {
    uniform_int_distribution<int> uid(1, 1e9);
    set<int> uniq;
    while (uniq.size() < N)
        uniq.insert(uid(rng));
    input.insert(input.end(), uniq.begin(), uniq.end());
    shuffle(input.begin(), input.end(), rng);

    cout << "INSERT" << endl;

    start = clock();
    for (int x : input)
        st.insert(x);
    cout << "PBDS: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    start = clock();
    for (int x : input)
        rbt.insert(x);
    cout << "Red Black: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;
}

void testFind() {
    uniform_int_distribution<int> uid(1, 1e9);
    vector<int> vals(input.begin(), input.end());
    vector<bool> ans1, ans2;
    for (int i=0; i<N; i+=2)
        vals[i] = uid(rng);
    shuffle(vals.begin(), vals.end(), rng);
    ans1.reserve(N);
    ans2.reserve(N);

    cout << "FIND" << endl;

    start = clock();
    for (int x : vals)
        ans1.push_back(st.find(x) != st.end());
    cout << "PBDS: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    start = clock();
    for (int x : vals)
        ans2.push_back(rbt.find(x) != rbt.tNil);
    cout << "Red Black: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    assert(ans1 == ans2);
}

void testRank() {
    vector<int> vals(input.begin(), input.end()), ans1, ans2;
    shuffle(vals.begin(), vals.end(), rng);
    ans1.reserve(N);
    ans2.reserve(N);

    cout << "RANK" << endl;

    start = clock();
    for (int x : vals)
        ans1.push_back(st.order_of_key(x));
    cout << "PBDS: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    start = clock();
    for (int x : vals)
        ans2.push_back(rbt.osRank(rbt.find(x)));
    cout << "Red Black: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    // order_of_key returns strictly smaller instead of less than or equal, so I decrement 1 from ans2 to compensate
    for (int &x : ans2)
        x--;
    assert(ans1 == ans2);
}

void testKth() {
    uniform_int_distribution<int> uid(0, (int) st.size() - 1);
    vector<int> vals(N), vals2, ans1, ans2;
    for (int &x : vals)
        x = uid(rng);
    vals2 = vals;
    for (int &x : vals2)
        x++;    // increment 1 cause RBTree is one-indexed
    ans1.reserve(N);
    ans2.reserve(N);

    cout << "KTH" << endl;

    start = clock();
    for (int x : vals)
        ans1.push_back(*st.find_by_order(x));
    cout << "PBDS: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    start = clock();
    for (int x : vals2)
        ans2.push_back(rbt.osSelect(rbt.getRoot(), x));
    cout << "Red Black: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    assert(ans1 == ans2);
}

void testErase() {
    cout << "ERASE" << endl;

    start = clock();
    for (int x : input)
        st.erase(x);
    cout << "PBDS: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

    start = clock();
    for (int x : input)
        rbt.erase(x);
    cout << "Red Black: " << (double) (clock() - start) / CLOCKS_PER_SEC << endl;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    testInsert();
    testFind();
    testRank();
    testKth();
    testErase();

    return 0;
}
