Problem:
Score : 600 points

Problem Statement

There is a function f(x), which is initially a constant function f(x) = 0.

We will ask you to process Q queries in order. There are two kinds of queries, update queries and evaluation queries, as follows:

An update query 1 a b: Given two integers a and b, let g(x) = f(x) + |x - a| + b and replace f(x) with g(x).

An evaluation query 2: Print x that minimizes f(x), and the minimum value of f(x). If there are multiple such values of x, choose the minimum such value.

We can show that the values to be output in an evaluation query are always integers, so we ask you to print those values as integers without decimal points.

Constraints

All values in input are integers.

1 \leq Q \leq 2 \times 10^5

-10^9 \leq a, b \leq 10^9

The first query is an update query.

Input

Input is given from Standard Input in the following format:

Q
Query_1
:
Query_Q

See Sample Input 1 for an example.

Output

For each evaluation query, print a line containing the response, in the order in which the queries are given.

The response to each evaluation query should be the minimum value of x that minimizes f(x), and the minimum value of f(x), in this order, with space in between.

Sample Input 1

4
1 4 2
2
1 1 -8
2

Sample Output 1

4 2
1 -3

In the first evaluation query, f(x) = |x - 4| + 2, which attains the minimum value of 2 at x = 4.

In the second evaluation query, f(x) = |x - 1| + |x - 4| - 6, which attains the minimum value of -3 when 1 \leq x \leq 4. Among the multiple values of x that minimize f(x), we ask you to print the minimum, that is, 1.

Sample Input 2

4
1 -1000000000 1000000000
1 -1000000000 1000000000
1 -1000000000 1000000000
2

Sample Output 2

-1000000000 3000000000

Solution in Rust (1.15.1) (judge verdict: Accepted)

#[doc = " https://github.com/hatoo/competitive-rust-snippets"]
#[allow(unused_imports)]
use std::cmp::{max, min, Ordering};
#[allow(unused_imports)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
#[allow(unused_imports)]
use std::io::{stdin, stdout, BufWriter, Write};
#[allow(unused_imports)]
use std::iter::FromIterator;
#[macro_export]
macro_rules ! dvec { ( $ t : expr ; $ len : expr ) => { vec ! [ $ t ; $ len ] } ; ( $ t : expr ; $ len : expr , $ ( $ rest : expr ) ,* ) => { vec ! [ dvec ! ( $ t ; $ ( $ rest ) ,* ) ; $ len ] } ; }
#[allow(unused_macros)]
macro_rules ! debug { ( $ ( $ a : expr ) ,* ) => { eprintln ! ( concat ! ( $ ( stringify ! ( $ a ) , " = {:?}, " ) ,* ) , $ ( $ a ) ,* ) ; } }
#[macro_export]
macro_rules ! input { ( source = $ s : expr , $ ( $ r : tt ) * ) => { let mut parser = Parser :: from_str ( $ s ) ; input_inner ! { parser , $ ( $ r ) * } } ; ( parser = $ parser : ident , $ ( $ r : tt ) * ) => { input_inner ! { $ parser , $ ( $ r ) * } } ; ( new_stdin_parser = $ parser : ident , $ ( $ r : tt ) * ) => { let stdin = std :: io :: stdin ( ) ; let reader = std :: io :: BufReader :: new ( stdin . lock ( ) ) ; let mut $ parser = Parser :: new ( reader ) ; input_inner ! { $ parser , $ ( $ r ) * } } ; ( $ ( $ r : tt ) * ) => { input ! { new_stdin_parser = parser , $ ( $ r ) * } } ; }
#[macro_export]
macro_rules ! input_inner { ( $ parser : ident ) => { } ; ( $ parser : ident , ) => { } ; ( $ parser : ident , $ var : ident : $ t : tt $ ( $ r : tt ) * ) => { let $ var = read_value ! ( $ parser , $ t ) ; input_inner ! { $ parser $ ( $ r ) * } } ; }
#[macro_export]
macro_rules ! read_value { ( $ parser : ident , ( $ ( $ t : tt ) ,* ) ) => { ( $ ( read_value ! ( $ parser , $ t ) ) ,* ) } ; ( $ parser : ident , [ $ t : tt ; $ len : expr ] ) => { ( 0 ..$ len ) . map ( | _ | read_value ! ( $ parser , $ t ) ) . collect ::< Vec < _ >> ( ) } ; ( $ parser : ident , chars ) => { read_value ! ( $ parser , String ) . chars ( ) . collect ::< Vec < char >> ( ) } ; ( $ parser : ident , usize1 ) => { read_value ! ( $ parser , usize ) - 1 } ; ( $ parser : ident , $ t : ty ) => { $ parser . next ::<$ t > ( ) . expect ( "Parse error" ) } ; }
use std::io;
use std::io::BufRead;
use std::str;
pub struct Parser<R> {
    reader: R,
    buf: Vec<u8>,
    pos: usize,
}
impl Parser<io::Empty> {
    pub fn from_str(s: &str) -> Parser<io::Empty> {
        Parser {
            reader: io::empty(),
            buf: s.as_bytes().to_vec(),
            pos: 0,
        }
    }
}
impl<R: BufRead> Parser<R> {
    pub fn new(reader: R) -> Parser<R> {
        Parser {
            reader: reader,
            buf: vec![],
            pos: 0,
        }
    }
    pub fn update_buf(&mut self) {
        self.buf.clear();
        self.pos = 0;
        loop {
            let (len, complete) = {
                let buf2 = self.reader.fill_buf().unwrap();
                self.buf.extend_from_slice(buf2);
                let len = buf2.len();
                if len == 0 {
                    break;
                }
                (len, buf2[len - 1] <= 0x20)
            };
            self.reader.consume(len);
            if complete {
                break;
            }
        }
    }
    pub fn next<T: str::FromStr>(&mut self) -> Result<T, T::Err> {
        loop {
            let mut begin = self.pos;
            while begin < self.buf.len() && (self.buf[begin] <= 0x20) {
                begin += 1;
            }
            let mut end = begin;
            while end < self.buf.len() && (self.buf[end] > 0x20) {
                end += 1;
            }
            if begin != self.buf.len() {
                self.pos = end;
                return str::from_utf8(&self.buf[begin..end]).unwrap().parse::<T>();
            } else {
                self.update_buf();
            }
        }
    }
}
#[allow(unused_macros)]
macro_rules ! debug { ( $ ( $ a : expr ) ,* ) => { eprintln ! ( concat ! ( $ ( stringify ! ( $ a ) , " = {:?}, " ) ,* ) , $ ( $ a ) ,* ) ; } }
#[doc = " https://github.com/hatoo/competitive-rust-snippets"]
const BIG_STACK_SIZE: bool = true;
#[allow(dead_code)]
fn main() {
    use std::thread;
    if BIG_STACK_SIZE {
        thread::Builder::new()
            .stack_size(32 * 1024 * 1024)
            .name("solve".into())
            .spawn(solve)
            .unwrap()
            .join()
            .unwrap();
    } else {
        solve();
    }
}
fn solve() {
    let out = stdout();
    let mut out = BufWriter::new(out.lock());

    input!{
        new_stdin_parser = parser,
        q: usize,
    }
    let mut A = Treap::new();
    let mut b_sum = 0;
    for _ in 0..q {
        input!{
            parser = parser,
            kind: usize,
        }
        if kind == 1 {
            input!{
                parser = parser,
                a: i64, b: i64,
            }
            A.orderd_insert(a);
            b_sum += b;
        } else if kind == 2 {
            let n = A.len();
            let mid = (n-1)/2;
            let x = A.get(mid);
            let mut sum_abs = 0;
            sum_abs += x*mid as i64 - A.sum(0,mid);
            sum_abs += A.sum(mid+1,n) - x*(n as i64 - 1 - mid as i64);
            println!("{} {}", x, sum_abs+b_sum);
        } else {
            unreachable!()
        }
    }
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Xorshift {
    seed: u64,
}
impl Xorshift {
    #[allow(dead_code)]
    pub fn new() -> Xorshift {
        Xorshift {
            seed: 0xf0fb588ca2196dac,
        }
    }
    #[allow(dead_code)]
    pub fn with_seed(seed: u64) -> Xorshift {
        Xorshift { seed: seed }
    }
    #[inline(always)]
    #[allow(dead_code)]
    pub fn next(&mut self) -> u64 {
        self.seed = self.seed ^ (self.seed << 13);
        self.seed = self.seed ^ (self.seed >> 7);
        self.seed = self.seed ^ (self.seed << 17);
        self.seed
    }
    #[inline(always)]
    #[allow(dead_code)]
    pub fn rand(&mut self, m: u64) -> u64 {
        self.next() % m
    }
    #[inline(always)]
    #[allow(dead_code)]
    pub fn randf(&mut self) -> f64 {
        use std::mem;
        const UPPER_MASK: u64 = 0x3FF0000000000000;
        const LOWER_MASK: u64 = 0xFFFFFFFFFFFFF;
        let tmp = UPPER_MASK | (self.next() & LOWER_MASK);
        let result: f64 = unsafe { mem::transmute(tmp) };
        result - 1.0
    }
}

mod treap {
    #[derive(Clone, Debug)]
    pub struct Node {
        pub v: i64,
        pri: u64,
        lch: Option<Box<Node>>,
        rch: Option<Box<Node>>,
        cnt: usize,
        sum: i64,
    }

    pub fn new_node(v: i64, rand: u64) -> Node {
        Node {
            v: v,
            pri: rand,
            lch: None.into(),
            rch: None.into(),
            cnt: 1,
            sum: v
        }
    }

    pub fn count(t: &Option<Box<Node>>) -> usize {
        match *t {
            Some(ref x) => x.cnt,
            None => 0,
        }
    }

    pub fn sum(t: &Option<Box<Node>>) -> i64 {
        match *t {
            Some(ref x) => x.sum,
            None => 0,
        }
    }

    fn update(t: Box<Node>) -> Box<Node> {
        let mut t = t;
        t.cnt = count(&t.lch) + count(&t.rch) + 1;
        t.sum = sum(&t.lch) + sum(&t.rch) + t.v;
        t
    }

    pub fn merge(l: Option<Box<Node>>, r: Option<Box<Node>>) -> Option<Box<Node>> {
        if l.is_none() && r.is_none() {
            return None
        }
        if l.is_none() {
            return r
        }
        if r.is_none() {
            return l
        }

        assert!(l.is_some() && r.is_some());
        let mut l = l.unwrap();
        let mut r = r.unwrap();

        if l.pri > r.pri {
            let old_rch = l.rch.take();
            l.rch = merge(old_rch, Some(r).into());
            update(l).into()
        } else {
            let old_lch = r.lch.take();
            r.lch = merge(Some(l).into(), old_lch);
            update(r).into()
        }
    }

    // [0,k),[k,n]
    pub fn split(t: Option<Box<Node>>, k: usize) -> (Option<Box<Node>>, Option<Box<Node>>) {
        if t.is_none() {
            return (None.into(), None.into())
        }
        let mut t = t.unwrap();

        let lcnt = count(&t.lch);
        if k <= lcnt {
            let old_lch = t.lch.take();
            let s = split(old_lch, k);
            t.lch = s.1;
            (s.0, Some(update(t)).into())
        } else {
            let old_rch = t.rch.take();
            let s = split(old_rch, k - lcnt - 1);
            t.rch = s.0;
            (Some(update(t)).into(), s.1)
        }
    }

    // split is fast but merge is slow
    pub fn insert(t: Box<Node>, k: usize, v: i64, rand: u64) -> Option<Box<Node>> {
        let (l,r) = split(Some(t).into(), k);
        let newt = merge(l, Some(new_node(v, rand).into()));
        let newt = merge(newt, r);
        newt
    }

    pub fn erase(t: Box<Node>, k: usize) -> Option<Box<Node>> {
        // [0,k),[k,k+1)[k+1,n)
        let (t1, rest) = split(Some(t).into(), k);
        let (t2, t3) = split(rest, 1);
        merge(t1, t3)
    }

    pub fn bisect(t: &Option<Box<Node>>, v: i64) -> usize {
        match *t {
            None => 0,
            Some(ref n) => {
                if v <= n.v {
                    bisect(&n.lch, v)
                } else {
                    count(&n.lch) + 1 + bisect(&n.rch, v)
                }
            }
        }
    }
}

struct Treap {
    rng: Xorshift,
    t: Option<Box<treap::Node>>,
}
impl Treap {
    pub fn new() -> Treap {
        Treap {
            rng: Xorshift::new(),
            t: None,
        }
    }
    #[doc = "[l,r)"]
    pub fn lower_bound(&mut self, l: usize, r: usize, v: i64) -> usize {
        let t = self.t.take();
        let (lt, t) = treap::split(t, l);
        let (t, rt) = treap::split(t, r-l);
        let idx = treap::count(&lt) + treap::bisect(&t, v);
        self.t = treap::merge(lt, treap::merge(t, rt));
        idx
    }
    pub fn orderd_insert(&mut self, v: i64) {
        if self.t.is_none() {
            self.insert(0, v);
        } else {
            let n = self.len();
            let ins_pos = self.lower_bound(0, n, v);
            self.insert(ins_pos, v);
        }
    }
    pub fn insert(&mut self, k: usize, v: i64) {
        if self.t.is_none() {
            self.t = Some(treap::new_node(v, self.rng.next()).into());
        } else {
            let t = self.t.take().unwrap();
            self.t = treap::insert(t, k, v, self.rng.next());
        }
    }
    pub fn erase(&mut self, k: usize) {
        if self.t.is_some() {
            let t = self.t.take().unwrap();
            self.t = treap::erase(t, k).into();
        }
    }
    #[doc = "split into [l,r)+[r,n)"]
    pub fn split(self, k: usize) -> (Treap, Treap) {
        let (a, b) = treap::split(self.t, k);
        (Treap { rng: self.rng.clone(), t: a }, Treap { rng: self.rng.clone(), t: b })
    }
    pub fn len(&self) -> usize {
        treap::count(&self.t)
    }
    #[doc = "[l,r)"]
    pub fn sum(&mut self, l: usize, r: usize) -> i64 {
        if self.t.is_none() {
            return 0
        } else {
            let t = self.t.take();
            // split into a1,b1,b2
            let (a1, a2) = treap::split(t, l);
            let (b1, b2) = treap::split(a2, r-l);
            let res = treap::sum(&b1);
            self.t = treap::merge(treap::merge(a1, b1), b2);
            res
        }
    }
    pub fn get(&mut self, k: usize) -> i64 {
        self.sum(k, k+1)
    }
}