use std::collections::BTreeMap;
use std::io::*;
use std::str::FromStr;

#[allow(dead_code)]
fn read<T: FromStr>() -> T {
    let stdin = stdin();
    let stdin = stdin.lock();
    let token: String = stdin
        .bytes()
        .map(|c| c.expect("failed to read char") as char)
        .skip_while(|c| c.is_whitespace())
        .take_while(|c| !c.is_whitespace())
        .collect();
    token.parse().ok().expect("failed to parse token")
}

fn insert(ps: &mut Vec<BTreeMap<i64, usize>>, offsets: &mut Vec<i64>, a: i64, cnt: i64, lr: usize) {
    let la = -*ps[0].iter().next().unwrap().0;
    let ra = *ps[1].iter().next().unwrap().0;
    if lr == 0 {
        let entry = ps[0].entry(-a).or_insert(0);
        *entry += 1;
        if a <= la {
            offsets[0] += (la - a).abs();
        } else {
            offsets[0] += (a - la).abs() * ((cnt + 1) / 2);
        }
    } else {
        let entry = ps[1].entry(a).or_insert(0);
        *entry += 1;
        if ra <= a {
            offsets[1] += (a - ra).abs();
        } else {
            offsets[1] += (ra - a).abs() * (cnt / 2);
        }
    }
}

fn push(ps: &mut Vec<BTreeMap<i64, usize>>, offsets: &mut Vec<i64>, a: i64, cnt: i64) {
    if cnt == 0 {
        ps[0].insert(-a, 1);
        return;
    }
    let la = -*ps[0].iter().next().unwrap().0;
    if cnt == 1 {
        if a < la {
            ps[1].insert(la, 1);
            ps[0].clear();
            ps[0].insert(-a, 1);
        } else {
            ps[1].insert(a, 1);
        }
        return;
    }
    let ra = *ps[1].iter().next().unwrap().0;
    if a < ra {
        insert(ps, offsets, a, cnt, 0);
        let la = -*ps[0].iter().next().unwrap().0;
        if cnt % 2 == 1 {
            *ps[0].iter_mut().next().unwrap().1 -= 1;
            if *ps[0].iter().next().unwrap().1 == 0 {
                ps[0].remove(&-la);
                let nla = -ps[0].iter().next().unwrap().0;
                offsets[0] -= (la - nla).abs() * ((cnt + 1) / 2);
            }
            insert(ps, offsets, la, cnt, 1);
        }
    } else {
        insert(ps, offsets, a, cnt, 1);
        let ra = *ps[1].iter().next().unwrap().0;
        if cnt % 2 == 0 {
            *ps[1].iter_mut().next().unwrap().1 -= 1;
            if *ps[1].iter().next().unwrap().1 == 0 {
                ps[1].remove(&ra);
                let nra = ps[1].iter().next().unwrap().0;
                offsets[1] -= (ra - nra).abs() * (cnt / 2);
            }
            insert(ps, offsets, ra, cnt, 0);
        }
    }
}

fn main() {
    let q = read::<usize>();
    let mut query = vec![(0, 0, 0); q];
    for i in 0..q {
        query[i].0 = read::<usize>();
        if query[i].0 == 1 {
            query[i].1 = read::<i64>();
            query[i].2 = read::<i64>();
        }
    }
    solve(&query);
}

fn solve(query: &Vec<(usize, i64, i64)>) -> String {
    let mut ret = "".to_string();
    let q = query.len();
    let mut cnt = 0;
    let mut b_sum = 0;
    let mut ps = vec![BTreeMap::<i64, usize>::new(); 2];
    let mut offsets = vec![0; 2];
    for i in 0..q {
        let t = query[i].0;
        if t == 1 {
            let a = query[i].1;
            let b = query[i].2;
            b_sum += b;
            push(&mut ps, &mut offsets, a, cnt);
            cnt += 1;
        } else {
            let lpos = if cnt == 0 {
                0
            } else {
                -*ps[0].iter().next().unwrap().0
            };
            let rpos = if cnt <= 1 {
                lpos
            } else {
                *ps[1].iter().next().unwrap().0
            };
            // println!("{:?} {} {}", offsets, lpos, rpos);
            let ans = offsets[0] + offsets[1] + cnt / 2 * (rpos - lpos).abs() + b_sum;
            println!("{} {}", lpos, ans);
            // ret += &format!("{} {}\n", lpos, ans);
        }
    }
    return ret;
}

fn naive(query: &Vec<(usize, i64, i64)>) -> String {
    let q = query.len();
    let mut ret = "".to_string();
    let mut b_sum = 0;
    let mut poss = vec![];
    for i in 0..q {
        if query[i].0 == 1 {
            poss.push(query[i].1);
            b_sum += query[i].2;
        } else {
            poss.sort();
            let mut ans = 1i64 << 60;
            let mut pos = poss[0];
            for &p1 in poss.iter() {
                let mut sum = b_sum;
                for &p2 in poss.iter() {
                    sum += (p1 - p2).abs();
                }
                if sum < ans {
                    ans = sum;
                    pos = p1;
                }
            }
            // println!("{} {}", pos, ans);
            ret += &format!("{} {}\n", pos, ans);
        }
    }
    return ret;
}

#[allow(dead_code)]
struct XorShift {
    x: u32,
    y: u32,
    z: u32,
    w: u32,
}
#[allow(dead_code)]
impl XorShift {
    fn new() -> XorShift {
        XorShift {
            x: 0x34fb2383,
            y: 0x327328fa,
            z: 0xabd4b54a,
            w: 0xa9dba8d1,
        }
    }
    fn seed(s: u32) -> XorShift {
        let mut rnd = XorShift::new();
        rnd.w = s;
        rnd
    }
    fn xor128(&mut self) -> u32 {
        let t = self.x ^ (self.x << 11);
        self.x = self.y;
        self.y = self.z;
        self.z = self.w;
        self.w = (self.w ^ (self.w >> 19)) ^ (t ^ (t >> 8));
        self.w
    }
    fn next_i32(&mut self, r: i32) -> i32 {
        (self.xor128() % r as u32) as i32
    }
    fn next_i32_lr(&mut self, l: i32, r: i32) -> i32 {
        self.next_i32(r - l + 1) + l
    }
    fn next_i64(&mut self, r: i64) -> i64 {
        ((((self.xor128() as u64) << 32) + self.xor128() as u64) % r as u64) as i64
    }
    fn next_i64_lr(&mut self, l: i64, r: i64) -> i64 {
        self.next_i64(r - l + 1) + l
    }
    fn next_usize_lr(&mut self, l: usize, r: usize) -> usize {
        self.next_i64_lr(l as i64, r as i64) as usize
    }
    fn next_f64(&mut self, r: f64) -> f64 {
        (self.xor128() as f64) / (0xffffffffu32 as f64) * r
    }
    fn next_f64_lr(&mut self, l: f64, r: f64) -> f64 {
        self.next_f64(r - l) + l
    }
}

#[test]
fn test() {
    let mut rnd = XorShift::new();
    for _iter in 0..100 {
        let q = rnd.next_usize_lr(4, 8);
        let mut query = vec![(0, 0, 0); q];
        for i in 0..q {
            if i % 2 == 0 {
                query[i].0 = 1;
                query[i].1 = rnd.next_i64_lr(0, 4);
                query[i].2 = 0;
            } else {
                query[i] = (2, 0, 0);
            }
        }
        let ret1 = solve(&query);
        let ret2 = naive(&query);
        if ret1 != ret2 {
            println!("{}", q);
            for i in 0..q {
                if query[i].0 == 1 {
                    println!("{} {} {}", query[i].0, query[i].1, query[i].2);
                } else {
                    println!("{}", query[i].0);
                }
            }
            println!("");
            println!("{}", ret1);
            println!("");
            println!("{}", ret2);
            assert!(false);
        }
    }
}


Measured memory_bytes: 14844