#include <iostream>

using namespace std;

// шаблонный класс, описывающий узел списка

template <typename KeyType>
class ListNode
{
public:
	ListNode( KeyType& key, ListNode* next ) { mKey = key; mNext = next; };
	KeyType& getKey() { return mKey; };
	ListNode* getNext() { return mNext; };
	void setNext( ListNode* node ) { mNext = node; };
protected:
	KeyType mKey;
	ListNode *mNext;
};

// шаблонный класс, описывающий список
template <typename KeyType, typename ListNodeType>
class List
{
public:
	List() { mHead = NULL; };
	virtual ~List() { clear(); };
	void clear();
	bool find( KeyType& key, ListNodeType*& foundNode, ListNodeType*& foundPrevNode );
	bool remove( KeyType& key );
	void push( KeyType& key ) { mHead = new ListNodeType( key, mHead ); };
	bool pop( KeyType& key );
	ListNodeType* getHead() { return mHead; };
	bool isEmpty() { return mHead == NULL; };
protected:
	ListNodeType* mHead;
};

// очищает все элементы списка и высвобождает память
template <typename KeyType, typename ListNodeType>
void List<KeyType,ListNodeType>::clear()
{
	ListNodeType *n =  mHead;
	while( NULL != n )
	{
	   ListNodeType *prev = n;
	   n = n->getNext();
	   delete prev;
	}
	mHead = NULL;
};

// находит узел списка по ключу
template <typename KeyType, typename ListNodeType>
bool List<KeyType,ListNodeType>::find( KeyType& key, ListNodeType*& foundNode, ListNodeType*& foundPrevNode )
{
	ListNodeType *n =  mHead;
	ListNodeType *prev = NULL;
	while( NULL != n )
	{
	   if ( n->getKey() == key )
	   {
	      foundNode = n;
	      foundPrevNode = prev;
	      return true;
	   }
	   prev = n;
	   n = n->getNext();
	}
	return false;
}

// удаляет узел списка по ключу
template <typename KeyType, typename ListNodeType>
bool List<KeyType,ListNodeType>::remove( KeyType& key )
{
	ListNodeType *n = NULL;
	ListNodeType *prev = NULL;

	if ( find( key, n, prev ) )
	{
	   if ( NULL != prev )
	   {
	      prev->setNext( n->getNext() );
	   }
	   else
	   {
	      mHead = n->getNext();
	   }
	   delete n;
	   return true;
	}
	return false;
};

// удаляет первый элемент
template <typename KeyType, typename ListNodeType>
bool List<KeyType,ListNodeType>::pop( KeyType& key )
{
	if ( mHead != NULL )
	{
	   key = mHead->getKey();
	   ListNodeType* n = mHead;
	   mHead = n->getNext();
	   delete n;
	   return true;
	}
	return false;
};

// Шаблонный класс, описывающий узел дерева двоичного поиска

template <typename KeyType, typename ValueType>
class TreeNode {
public:
	TreeNode( KeyType& key ) : mKey(key), mValue(NULL), mParent(NULL), mLeft(NULL), mRight(NULL) {};
	TreeNode( KeyType& key, ValueType& value ) : mKey(key), mValue(value), mParent(NULL), mLeft(NULL), mRight(NULL) {};
	~TreeNode() { };
	void setLeft( TreeNode* left ) { mLeft = left; };
	void setRight( TreeNode* right ) { mRight = right; };
	void setParent( TreeNode* parent ) { mParent = parent; };
	KeyType getKey() { return mKey; };
	ValueType& getValue() { return mValue; };
	void setValue( ValueType& value ) { mValue = value; };
	TreeNode* getLeft() { return mLeft; };
	TreeNode* getRight() { return mRight; };
	TreeNode* getParent() { return mParent; };
	virtual string getValueAsText() { return ""; };
protected:
	KeyType mKey;
	ValueType mValue;
	TreeNode* mParent;
	TreeNode* mLeft;
	TreeNode* mRight;
};

// шаблонный класс, описывающий дерево
template <typename KeyType, typename NodeType>
class Tree {
	NodeType* mRoot;
public:
	Tree() { mRoot = NULL; };
	~Tree() { clear(); };
	void clear() { dispose( mRoot ); mRoot = NULL; };
	bool isEmpty() { return mRoot == NULL; };
	NodeType* getRoot() { return mRoot; };
	NodeType* findOrInsert( KeyType& key, bool insertIfAbsent );
	void exclude( NodeType* node );
	void remove( NodeType* node ) { exclude( node ); dispose( node ); };
	bool remove( KeyType& key, NodeType* removedNodeCopy );
	bool remove( KeyType& key ) { return( key, NULL ); };
	NodeType* findMax( NodeType* node );
	NodeType* findMax() { return findMax( mRoot ); };
protected:
	void dispose( NodeType* node );
};

// находит узел двоичного дерева по ключу или всталяет, если не найдено
template <typename KeyType, typename NodeType>
NodeType* Tree<KeyType,NodeType>::findOrInsert( KeyType& key, bool insertIfAbsent )
{
	enum EInsertAction
	{
	   eNONE,
	   eROOT,
	   eLEFT,
	   eRIGHT
	};

	EInsertAction doInsert = eNONE;
	NodeType *p = mRoot;

	if ( p != NULL )
	{
	   for(;;)
	   {
	      if ( key < p->getKey() )
	      {
	         if ( p->getLeft() != NULL )
	         {
	            p = p->getLeft();
	         }
	         else
	         {
	            doInsert = eLEFT;
	            break;
	         }
	      }
	      else
	      if ( key > p->getKey() )
	      {
	         if ( p->getRight() != NULL )
	         {
	            p = p->getRight();
	         }
	         else
	         {
	            doInsert = eRIGHT;
	            break;
	         }
	      }
	      else
	      {
	         return p;
	      }
	   }
	}
	else
	{
	   doInsert = eROOT;
	}

	if ( doInsert != eNONE && insertIfAbsent )
	{
	   NodeType* q = new NodeType( key );
	   switch( doInsert )
	   {
	      case eLEFT : p->setLeft( q ); break;
	      case eRIGHT: p->setRight( q ); break;
	      case eROOT : mRoot = q; break;
	      default: break;
	   }
	   q->setParent( p );
	   return q;
	}

	return NULL;
}

// находит узел двоичного дерева с максимальным значением ключа
template <typename KeyType, typename NodeType>
NodeType* Tree<KeyType,NodeType>::findMax( NodeType* node )
{
	if ( node != NULL )
	{
	   NodeType* n = node;
	   while( NULL != n->getRight() )
	   {
	      n = n->getRight();
	   }
	   return n;
	}
	return NULL;
}

// исключает узел двоичного дерева из дерева, не удаляя
template <typename KeyType, typename NodeType>
void Tree<KeyType,NodeType>::exclude( NodeType* node )
{
	if ( NULL != node )
	{
	   NodeType* successor = NULL;

	   if ( node->getLeft() == NULL )
	   {
	      successor = node->getRight();
	   }
	   else
	   if ( node->getRight() == NULL )
	   {
	      successor = node->getLeft();
	   }
	   else
	   {
	      successor = node->getRight();
	      NodeType* left = successor->getLeft();

	      while( left != NULL )
	      {
	         successor = left;
	         left = successor->getLeft();
	      }

	      if ( successor->getParent() != node )
	      {
	     	NodeType* right = successor->getRight();
	         successor->getParent()->setLeft( right );
	         if ( NULL != right)
	         {
	         	successor->getRight()->setParent( successor->getParent() );
	         }
	         successor->setRight( node->getRight() );
	         node->getRight()->setParent( successor );
	      }

	      successor->setLeft( node->getLeft() );
	      node->getLeft()->setParent( successor );
	   }
	   
	   if ( node->getParent() == NULL )
	   {
	      mRoot = successor;
	   }
	   else
	   if ( node->getParent()->getLeft() == node )
	   {
	      node->getParent()->setLeft( successor );
	   }
	   else
	   if ( node->getParent()->getRight() == node )
	   {
	      node->getParent()->setRight( successor );
	   }
	   else
	   {
	      cout << "fatal error: orphaned# " 
	         << "N:" << node << ",P:" << node->getParent() << ",L:" << node->getLeft() << ",R:"<<node->getRight()
	         << endl;
	      return;
	   }
	   
	   if ( NULL != successor )
	   {
	      successor->setParent( node->getParent() );
	   }
	   
	   node->setParent( NULL );
	   node->setLeft( NULL );
	   node->setRight( NULL );
	}
}

// находит и удаляет узел дерева по ключу
template <typename KeyType, typename NodeType>
bool Tree<KeyType,NodeType>::remove( KeyType& key, NodeType* removedNodeCopy )
{
	NodeType* node = findOrInsert( key, false );
	if ( node != NULL )
	{
	   exclude( node );
	   if ( removedNodeCopy != NULL )
	   {
	      *removedNodeCopy = node;
	   }
	   dispose( node );
	   return true;
	}
	return false;
}

// удаляет узел из памяти
template <typename KeyType, typename NodeType>
void Tree<KeyType,NodeType>::dispose( NodeType* node )
{
	if ( node != NULL ) 
	{
	    dispose( node->getLeft() );
	    dispose( node->getRight() );
	    delete node;
	}
}

// класы-контейнеры на основе высше описанных шаблонных классов

class PrioTreeNode;
typedef TreeNode<string,PrioTreeNode*> IdTreeNodeBase;
class IdTreeNode : public IdTreeNodeBase
{
public:
	IdTreeNode( string& id ) : IdTreeNodeBase( id ) {};
	virtual ~IdTreeNode() { mValue = NULL; };
	IdTreeNode* getLeft() { return (IdTreeNode*)mLeft; };
	IdTreeNode* getRight() { return (IdTreeNode*)mRight; };
	IdTreeNode* getParent() { return (IdTreeNode*)mParent; };
};
typedef Tree<string,IdTreeNode> IdTree;

typedef ListNode<IdTreeNode*> IdListNode;
typedef List<IdTreeNode*,IdListNode> IdList;

typedef TreeNode<int,IdList*> PrioTreeNodeBase;
class PrioTreeNode : public PrioTreeNodeBase
{
public:
	PrioTreeNode( int& prio ) : PrioTreeNodeBase( prio ) { mValue = new IdList(); };
	virtual ~PrioTreeNode() { delete mValue; };
	PrioTreeNode* getLeft() { return (PrioTreeNode*)mLeft; };
	PrioTreeNode* getRight() { return (PrioTreeNode*)mRight; };
	PrioTreeNode* getParent() { return (PrioTreeNode*)mParent; };
};
typedef Tree<int,PrioTreeNode> PrioTree;

// класс, реализующий очередь с приоритетами

class PrioQueue
{

public:

	PrioQueue() { }
	~PrioQueue() { }

	bool add( string& id, int prio  );
	bool pop( string& id, int& prio );
	bool change( string& id, int prio );
	void clear();

private:

	IdTree mIdTree;
	PrioTree mPrioTree;

};

bool PrioQueue::add( string& id, int prio  )
{
	IdTreeNode *idNode = mIdTree.findOrInsert( id, true );
	if ( NULL != idNode )
	{
	   PrioTreeNode* prioNode = mPrioTree.findOrInsert( prio, true );
	   if ( NULL != prioNode )
	   {
	      prioNode->getValue()->push( idNode );
	      idNode->setValue( prioNode );
	      return true;
	   }
	}
	return false;
}

bool PrioQueue::pop( string& id, int& prio )
{
	bool result = false;
	PrioTreeNode* prioNode = mPrioTree.findMax();
	if ( NULL != prioNode )
	{
	   prio = prioNode->getKey();
	   if ( ! prioNode->getValue()->isEmpty() )
	   {
	      IdTreeNode* idNode;
	      if ( prioNode->getValue()->pop( idNode ) )
	      {
	         id = idNode->getKey();
	         mIdTree.remove( idNode );
	         if ( prioNode->getValue()->isEmpty() )
	         {
	            mPrioTree.remove( prioNode );
	         }
	         result = true;
	      }
	   }
	}
	return result;
}

bool PrioQueue::change( string& id, int prio )
{
	bool result = false;
	IdTreeNode* idNode = mIdTree.findOrInsert( id, false );
	if ( NULL != idNode )
	{
	   PrioTreeNode* oldPrioNode = idNode->getValue();
	   (void)oldPrioNode->getValue()->remove( idNode );
	   if ( oldPrioNode->getValue()->isEmpty() )
	   {
	      mPrioTree.remove( oldPrioNode );
	   }
	   PrioTreeNode* newPrioNode = mPrioTree.findOrInsert( prio, true );
	   idNode->setValue( newPrioNode );
	   newPrioNode->getValue()->push( idNode );
	   result = true;
	}
	return result;
}

void PrioQueue::clear()
{
	mIdTree.clear();
	mPrioTree.clear();
}

int main( int argc, char** argv )
{
	PrioQueue q;

	string command;
	string id;
	int prio;

	while ( cin >> command )
	{
	   if ( command.compare( 0, 1, "#" ) == 0 ) continue;
	   else
	   if ( command == "add" || command == "ADD" )
	   {
	      cin >> id;
	      cin >> prio;

	      q.add( id, prio );
	   }
	   else
	   if ( command == "change" || command == "CHANGE" )
	   {
	      cin >> id;
	      cin >> prio;

	      q.change( id, prio );
	   }
	   else
	   if ( command == "pop" || command == "POP" )
	   {
	      if ( q.pop( id, prio ) )
	      {
	         cout << id << " " << prio << endl;
	      }
	      else
	      {
	         cout << "error" << endl;
	      }
	   }
	   else
	   if ( command == "clear" || command == "CLEAR" )
	   {
	      q.clear();
	   }
	   else
	   if ( command == "exit" || command == "EXIT" )
	   {
	      cout << "bye" << endl;
	      return 0;
	   }
	}

	return 0;
}
