// Randomized implicit Cartesian tree
// Added mass change: reverse a segment
// Note: relax() in tPrintRecur to see the intended tree,
// remove the relax() call to see the actual tree
// #define _GLIBCXX_DEBUG 1
#include <bits/stdc++.h>
using namespace std;

struct Node
{
	string value;
	int size;
	int total;
	bool rev;
	Node* left;
	Node* right;
	
	void recalc ()
	{
		size = 1;
		if (left != nullptr) size += left->size;
		if (right != nullptr) size += right->size;
		total = value[0] - 'A';
		if (left != nullptr) total += left->total;
		if (right != nullptr) total += right->total;
	}
	
	void relax ()
	{
		if (rev)
		{
			swap (left, right);
			if (left) left->rev ^= true;
			if (right) right->rev ^= true;
			rev = false;
		}
	}
	
	Node (string value_) : value (value_), size (1),
   	    total (value_[0] - 'A'), rev (false), left (nullptr), right (nullptr)
	{}
};

pair <Node*, Node*> tSplitSize (Node* t, int k)
{ // [0..k) | [k..oo)
	if (t == nullptr) return {nullptr, nullptr};
	t->relax();
	auto tLeftSize = t->left ? t->left->size : 0;
	if (k <= tLeftSize) // k < (tLeftSize + 1)
	{
		auto temp = tSplitSize (t->left, k);
		t->left = temp.second;
		t->recalc();
		return {temp.first, t};
	}
	else
	{
		auto temp = tSplitSize (t->right, k - (tLeftSize + 1));
		t->right = temp.first;
		t->recalc();
		return {t, temp.second};
	}
}

int random (int limit)
{
	return unsigned ((rand () << 15) ^ rand ()) % limit;
}

Node* tMerge (Node* l, Node* r)
{
	if (l == nullptr) return r;
	if (r == nullptr) return l;
	l->relax();
	r->relax();
//	if (l->size > r->size)
	if ((int) random (l->size + r->size) < l->size)
	{
		l->right = tMerge (l->right, r);
		l->recalc();
		return l;
	}
	else
	{
		r->left = tMerge (l, r->left);
		r->recalc();
		return r;
	}
}

Node * tInsert (Node* t, int pos, string value)
{
	pair <Node*, Node*> temp = tSplitSize (t, pos);
	Node* v = new Node (value);
	Node* half = tMerge (temp.first, v);
	return tMerge (half, temp.second);
}

string tFindPos (Node* t, int k)
{
	if (t == nullptr) assert (false);
	auto tLeftSize = t->left ? t->left->size : 0;
	if (k < tLeftSize)
		return tFindPos (t->left, k);
	k -= tLeftSize;
	if (k == 0) return t->value;
	k -= 1;
	return tFindPos (t->right, k);
}

void tPrintRecur (Node* t)
{
	if (t == nullptr) return;
	t->relax ();
//	cout << "(";
	tPrintRecur (t->left);
	cout << t->value;
//	cout << t->value << "|" << t->size << "";
	tPrintRecur (t->right);
//	cout << ")";
}

void tPrint (Node* t)
{
	tPrintRecur (t);
	cout << endl;
}

int main ()
{
	int limit;
	cin >> limit;
	srand(time(nullptr));
	Node* root = nullptr;
	for (int i = 0; i < limit; i++)
	{
		root = tInsert (root, i, string () +
		    char (i % 26 + 'A'));
		if (root->size <= 10) tPrint (root);
		cout << root->total << endl;
	}
	tPrint (root);
	for (int i = 0; i < limit; i++)
	{
		cout << tFindPos (root, i);
	}
	cout << endl;
/*
	auto temp = tSplitSize (root, root->size / 2);
	root = tMerge (temp.second, temp.first);
	tPrint (root);
*/

	auto two = tSplitSize (root, limit * 2/ 3); // hi
	auto one = tSplitSize (two.first, limit / 3); // lo
	// one.first (lo) | one.second (hi - lo) | two.second (...)
	cout << one.second->total << endl;
	one.second->rev ^= true;
	two.first = tMerge (one.first, one.second);
	root = tMerge (two.first, two.second);
	tPrint (root);
}
