/// Implicit Randomized Treap (Cartesian Tree)
///   Simulate x (array index), simulate y (priority)
///   Store value
///   Maintain size of subtree
///   total: sum of values in subtree
///   Maintain reverse bit: a promise to reverse a segment
/// Base Functions
///   tSplitPos: split into first pos elements and the rest
///   tMerge: merge two trees
/// Array Interface
///   tInsertPos: insert value at position pos
///   tFindPos: return value at position pos
/// Debugging
///   tOutput: print bracket structure of tree
/// Example Usage
///   Add 20 elements to the middle
///   Take array segments [0..10), [1, 11), ..., [9, 19) and reverse
///   (disabled) Relax and output after every operation
///   Relax and output after all operations
///   Find values of all elements
///   Split into two parts, check sizes
#include <bits/stdc++.h>
using namespace std;

auto gen = mt19937 (time (0));
using uniform = uniform_int_distribution <int>;

struct Node;
using PNode = Node *;
int getSize (PNode t);
using Num = long long;
Num getTotal (PNode t);
struct Node
{
	Num value;
	Num total;
	int size;
	bool rev;
	PNode left;
	PNode right;
	
	void recalc ()
	{
		size = 1 + getSize (left) + getSize (right);
		total = value + getTotal (left) + getTotal (right);
	}
	
	void relax ()
	{
		if (rev)
		{
			swap (left, right);
			if (left != nullptr) left -> rev ^= true;
			if (right != nullptr) right -> rev ^= true;
			rev = false;
		}
	}

	Node (Num value_)
	{
		value = value_;
		total = value_;
		size = 1;
		rev = false;
		left = nullptr;
		right = nullptr;
	}

	~Node ()
	{
		if (left != nullptr) delete left;
		if (right != nullptr) delete right;
	}
};

int getSize (PNode t)
{
	return t == nullptr ? 0 : t -> size;
}

Num getTotal (PNode t)
{
	return t == nullptr ? Num (0) : t -> total;
}

pair <PNode, PNode> tSplitPos (PNode t, int pos)
{
	if (t == nullptr) return {nullptr, nullptr};
	t -> relax ();
	auto leftSize = getSize (t -> left);
	if (pos < leftSize + 1)
	{
		auto p = tSplitPos (t -> left, pos);
		t -> left = p.second;
		t -> recalc ();
		return {p.first, t};
	}
	else
	{
		auto p = tSplitPos (t -> right, pos - leftSize - 1);
		t -> right = p.first;
		t -> recalc ();
		return {t, p.second};
	}
}

PNode tMerge (PNode l, PNode r)
{
	if (l == nullptr) return r;
	if (r == nullptr) return l;
	l -> relax ();
	r -> relax ();
	if (uniform (0, l -> size + r -> size - 1) (gen) < l -> size)
	{
		l -> right = tMerge (l -> right, r);
		l -> recalc ();
		return l;
	}
	else
	{
		r -> left = tMerge (l, r -> left);
		r -> recalc ();
		return r;
	}
}

PNode tInsertPos (PNode t, int pos, Num value)
{
	auto v = new Node (value);
	auto p = tSplitPos (t, pos);
	auto half = tMerge (p.first, v);
	return tMerge (half, p.second);
}

void tOutput (PNode t, bool doEndl = true)
{
	if (t != nullptr)
	{
		t -> relax ();
		cout << '(';
		tOutput (t -> left, false);
		cout << t -> value;
//		cout << t -> value <<
//		    '|' << t -> size << '"' << t -> total;
		tOutput (t -> right, false);
		cout << ')';
	}
	if (doEndl) cout << '.' << endl;
}

Num tFindPos (PNode t, int pos)
{
	if (t == nullptr) return Num (-1);
	t -> relax ();
	auto leftSize = getSize (t -> left);
	if (leftSize == pos) return t -> value;
	if (leftSize > pos) return tFindPos (t -> left, pos);
	else return tFindPos (t -> right, pos - leftSize - 1);
}

int main ()
{
/*
	Node u;
	u.left; u.right;
	PNode v;
	(*v).left; (*v).right;
	v -> left; v -> right;
*/
	int const limit = int (20);
	PNode root = nullptr;
	
	for (int x = 0; x < limit; x++)
	{
		root = tInsertPos (root, x, x);
		if (x < 20) tOutput (root);
	}
	
	for (int x = 0; x < limit / 2; x++)
	{
		auto p = tSplitPos (root, x + limit / 2);
		auto q = tSplitPos (p.first, x);
		q.second -> rev ^= true;
		p.first = tMerge (q.first, q.second);
		root = tMerge (p.first, p.second);
//		tOutput (root);
	}
	tOutput (root);
	
	for (int x = 0; x < limit; x++)
	{
		auto res = tFindPos (root, x);
		if (x < 5) cout << x << ' ' << res << endl;
	}
	
	{
		auto p = tSplitPos (root, limit - 5);
		cout << "sizes: " << getSize (p.first) << " " <<
		    getSize (p.second) << endl;
		root = tMerge (p.first, p.second);
	}

	return 0;
}
