#include<bits/stdc++.h>
using namespace std;
#define INT long long
#define ll long long

const int maxn = 201111;
template<class val_t>
class TreeArray {
    val_t* a;
    int maxn;
public:
    ~TreeArray()
    {
        delete[](a);
    }
    void Init(size_t siz) 
    {
        delete[](a);
        maxn = siz;
        a = new val_t[siz + 8];
    }
    int lowbit(int x)
    {
        return x & (-x);
    }
    void modify(int x,val_t add)
    { 
        while(x<=maxn) 
        {     
            a[x]+=add;   
            x+=lowbit(x);
        }
    }
    val_t get_sum(int x)
    { 
        val_t ret=0;
        while(x) 
        {      
            ret+=a[x];  
            x-=lowbit(x);  
        } 
        return ret;
    }
};
struct Query
{
    int a,b,t;
    int id;
};
const double alpha = 0.75;
template<class val_t>
class STree {
public:

    struct Node {
        Node * ch[2];
        val_t key;
        int size, cover; // size为有效节点的数量，cover为节点总数量 
        bool exist; // 是否存在（即是否被删除） 
        void PushUp()
        {
            size = ch[0]->size + ch[1]->size + (int)exist;
            cover = ch[0]->cover + ch[1]->cover + 1;
        }
        bool isBad() // 判断是否需要重构 
        { 
            return ((ch[0]->cover > cover * alpha + 5) || 
                    (ch[1]->cover > cover * alpha + 5));
        }
    };
protected:
    Node* mem_poor; //内存池，栈上可能分配不下，init时由new自动分配
    Node *tail, *root, *null; // 用null表示NULL的指针更方便，tail为内存分配指针，root为根 
    Node* *bc; int bc_top; // 储存被删除的节点的内存地址，分配时可以再利用这些地址 

    Node * NewNode(val_t key) {
        Node * p = bc_top ? bc[--bc_top] : tail++;
        p->ch[0] = p->ch[1] = null;
        p->size = p->cover = 1; p->exist = true;
        p->key = key;
        return p;
    }
    void Travel(Node * p, vector<Node *>&v) {
        if (p == null) return;
        Travel(p->ch[0], v);
        if (p->exist) v.push_back(p); // 构建序列 
        else bc[bc_top++] = p; // 回收 
        Travel(p->ch[1], v);
    }
    Node * Divide(vector<Node *>&v, int l, int r) {
        if (l >= r) return null;
        int mid = (l + r) >> 1;
        Node * p = v[mid];
        p->ch[0] = Divide(v, l, mid);
        p->ch[1] = Divide(v, mid + 1, r);
        p->PushUp(); // 自底向上维护，先维护子树 
        return p;
    }
    void Rebuild(Node * &p) {
        static vector<Node *>v; v.clear();
        Travel(p, v); p = Divide(v, 0, v.size());
    }
    Node ** Insert(Node *&p, val_t val) {
        if (p == null) {
            p = NewNode(val);
            return &null;
        }
        else {
            p->size++; p->cover++;

            // 返回值储存需要重构的位置，若子树也需要重构，本节点开始也需要重构，以本节点为根重构 
            Node ** res = Insert(p->ch[val >= p->key], val);
            if (p->isBad()) res = &p;
            return res;
        }
    }
    void Erase(Node *p, int id) {
        p->size--;
        int offset = p->ch[0]->size + p->exist;
        if (p->exist && id == offset) {
            p->exist = false;
            return;
        }
        else {
            if (id <= offset) Erase(p->ch[0], id);
            else Erase(p->ch[1], id - offset);
        }
    }
public:
    void Init(size_t siz) {
        delete[](mem_poor);
        delete[](bc);
        mem_poor = new Node[siz+10];
        bc = new Node*[siz+10];
        tail = mem_poor;
        null = tail++;
        null->ch[0] = null->ch[1] = null;
        null->cover = null->size = null->key = 0;
        root = null; bc_top = 0;
    }
    STree():mem_poor(NULL),bc(NULL) {}
    ~STree()
    {
        delete[](mem_poor);
        delete[](bc);
    }
    void Insert(val_t val) {
        Node ** p = Insert(root, val);
        if (*p != null) Rebuild(*p);
    }
    int Rank(val_t val) {
        Node * now = root;
        int ans = 1;
        while (now != null) { // 非递归求排名 
            if (now->key >= val) now = now->ch[0];
            else {
                ans += now->ch[0]->size + now->exist;
                now = now->ch[1];
            }
        }
        return ans;
    }
    val_t Kth(int k) {
        Node * now = root;
        while (now != null) { // 非递归求第K大 
            if (now->ch[0]->size + 1 == k && now->exist) return now->key;
            else if (now->ch[0]->size >= k) now = now->ch[0];
            else k -= now->ch[0]->size + now->exist, now = now->ch[1];
        }
        return -1;//error
    }
    void Erase(val_t k) {
        Erase(root, Rank(k));
        if (root->size < alpha * root->cover) Rebuild(root);
    }
    void Erase_kth(int k) {
        Erase(root, k);
        if (root->size < alpha * root->cover) Rebuild(root);
    }
};
int main()
{
    int q;
    cin>>q;
    vector<Query> qs(q);
    vector<pair<ll,int>> pre_hash;
    map<pair<ll,int>,int> hash;
    for(int i=0;i<q;++i)
    {
        cin>>qs[i].t;
        if(qs[i].t == 1){
            cin>>qs[i].a>>qs[i].b;
            pre_hash.push_back({qs[i].a,i});
        }
    }
    sort(pre_hash.begin(),pre_hash.end());
    for(int i=0;i<pre_hash.size();++i)
    {
        hash[pre_hash[i]] = i+1;
    }
    ll b = 0;
    STree<ll> st;
    TreeArray<ll> tarray;
    st.Init(maxn);
    tarray.Init(maxn);
    int cnt = 0;
    for(int i=0;i<q;++i)
    {
        if(qs[i].t == 1)
        {
            tarray.modify(hash[{qs[i].a,i}],qs[i].a);
            b += qs[i].b;
            st.Insert(hash[{qs[i].a,i}]);
            cnt++;
        }else
        {
            int id = st.Kth((cnt+1)/2);
            ll num = pre_hash[id-1].first;
            ll sum1 = tarray.get_sum(maxn) - tarray.get_sum(id-1);
            ll ret = sum1 - num*(cnt - (cnt+1)/2 + 1);
            ll sum2 = tarray.get_sum(id-1);
            ret += num*((cnt+1)/2-1) - sum2;
            ret += b;
            cout<<num<<" "<<ret<<endl;
        }
    }
}

Measured memory_bytes: 32876