Messy Medians
Difficulty: Hard

Robin is stuck doing a terminally boring data entry job. She has to enter a lot of numeric observations manually and report running medians after each step to her project lead. The task is sufficiently mind-numbing for her to make a lot of entry errors. Fortunately, she has a keen eye and often notices her mistakes. On the other hand, sometimes she makes mistakes while spotting - or correcting - previous mistakes, and so on.  
<br>
Your task is to implement a program that will help Robin finish this boring job as soon as possible. The program will accept user input and report a running median for all the observations entered so far. It should also be capable of rolling back to any previous state (including those that were previously rolled back on their own).

**Input Format**

First line contains an integer,*T*, the total number of lines to follow.  
<br>
Each of the following *T* lines contains either:

1. a positive integer *N* that will contribute in calculation of the current running median;

2. or a request to roll back to a previous state, which is represented by a negative number between -1 and -*M*, where *M* is the total number of queries executed so far. -1 requests a return to the immediately preceding state (which actually results in no state change), while -*M* requests a return to the state after entering the very first number.

**Output Format**

Output *T* integers, representing current running medians after performing operations on corresponding lines in the input.  

**Constraints**

1 &le; _T_ &le; 10<sup>5</sup>  
1 &le; _N_ &le; 10<sup>9</sup>  
1 &le; _M_ &le; number of queries executed so far. 

**Notes**

* You will never be asked to roll back to a non-existent state, or a state for which the running median is not well-defined. In particular, the first line after *T* will always be a positive number.  

* You should print out the median after executing each query, including rollbacks.  

* For collections with even number of elements we report the smaller of the two candidate medians.



**Sample Input**

    10
    1
    5
    -2
    3
    2
    5
    4
    -7
    2
    -3

**Sample Output**

    1
    1
    1
    1
    2
    2
    3
    1
    1
    3

**Explanation**

(This represents the collection of the numbers tracked after each step, with median in bold.)

Step 1: 1 -> [**1**]

Step 2: 5 -> [**1**, 5]

Step 3: request a return to state after step (3 - 2) = 1 -> [**1**]

Step 4: 3 -> [**1**, 3]

Step 5: 2 -> [1, **2**, 3]

Step 6: 5 -> [1, **2**, 3, 5]

Step 7: 4 -> [1, 2, **3**, 4, 5]

Step 8: request a return to state after step (8 - 7) = 1 -> [**1**]

Step 9: 2 -> [**1**, 2]

Step 10: request a return to state after step (10 - 3) = 7 -> [1, 2, **3**, 4, 5]

---

**Tested by:** [Abhiranjan Kumar](/abhiranjan)


Input Format:

Output Format:

Sample Input:

Sample Output:

Python 3 starter code:


Reference solution (Python):
**Approach #1**

	module Main where
    import Data.List
    import Data.Char
    import qualified Data.Vector as V
    import qualified Data.Map as Map
    import Text.Printf

    data SegmentTree = Leaf {
                            count     :: Int
                          , index     :: Int
                        }
                      | Node {
                            count     :: Int
                          , leftIdx   :: Int
                          , rightIdx  :: Int
                          , leftTree  :: SegmentTree
                          , rightTree :: SegmentTree
                        }


    initialiseTree :: Int -> SegmentTree
    initialiseTree = createNode 1
      where
        createNode l r
          | l == r = Leaf 0 l
          | otherwise = Node 0 l r (createNode l mid) (createNode (mid+1) r)
            where
              mid = (l+r) `div` 2

    insertNode :: Int -> SegmentTree -> SegmentTree
    insertNode val lf@(Leaf cnt idx) = if idx == val then lf{count = cnt+1} else lf
    insertNode val nd@(Node cnt lIdx rIdx lTree rTree)
      | lIdx <= val && val <= rIdx = nd {count = cnt+1, leftTree = insertNode val lTree, rightTree = insertNode val rTree}
      | otherwise = nd

    getElements :: Int -> SegmentTree -> Int
    getElements val (Leaf cnt idx) = if idx <= val then cnt else 0
    getElements val (Node cnt lIdx rIdx lTree rTree)
      | lIdx > val = 0
      | rIdx < val = cnt
      | otherwise = getElements val lTree + getElements val rTree

    getMedian :: SegmentTree -> Int
    getMedian root = binarySearch (leftIdx root) (rightIdx root)
      where
        cnt = (count root + 1)`div`2
        binarySearch l r
          | l == r = l
          | getElements mid root >= cnt = binarySearch l mid
          | otherwise = binarySearch (mid+1) r
            where mid = (l+r+0) `div` 2

    main :: IO ()
    main = getContents >>= mapM_ print. solve. map read. words

    solve (n:arr)
      | n /= length arr = error $ "length arr = " ++ show (length arr)
      | any (\x -> x == 0 || x > 10^9) arr = error $ "arr = " ++ show arr
      | otherwise =   map getIdx [  getMedian (trees V.! i) |i <- [1..len]]
      where
        ans         = [ (indAns i, trees V.! i) | i <- [1..len]]
        indAns i    = getIdx $ getMedian (trees V.! i)
        root        = initialiseTree. length $ sarr
        vArr        = V.fromList arr
        trees       = V.fromList $ root : [ let v = vArr V.! (i - 1) in
                          if v > 0 then
                            insertNode (getRIdx v) (trees V.! (i-1))
                            else trees V.! (i+v)
                        | i <- [1..len]
                      ]
        sarr        = map head. group. dropWhile (<= 0). sort $ arr
        len         = length arr
        idx, revIdx :: Map.Map Int Int
        revIdx      = Map.fromList. zip sarr $ [1..]
        idx         = Map.fromList. zip [(1::Int)..] $  sarr
        getRIdx key = Map.findWithDefault (-1) key revIdx
        getIdx key  = Map.findWithDefault (-1) key idx
        
---

**Approach #2**

    import qualified Data.Vector as V
    import qualified Data.MultiSet as MultiSet
    import Text.Printf                           -- printf "%0.6f" (1.0)

    data MSet a = MSet {
          mSet :: MultiSet.MultiSet a
        , size :: Int
      }-- deriving (Show)

    instance Show a => Show (MSet a) where
      show (MSet st sz) = printf "%d: %s" sz (show.MultiSet.toList $ st)

    emptyMSet :: MSet a
    emptyMSet = MSet MultiSet.empty 0

    insertIntoMSet, deleteFromMSet :: (Show a, Ord a) => a -> MSet a -> MSet a
    insertIntoMSet x (MSet st sz) = MSet (MultiSet.insert x st) (sz+1)
    deleteFromMSet x (MSet st sz)
      | MultiSet.member x st  = MSet (MultiSet.delete x st) (sz-1)
      | otherwise             = error $ printf "member %s not found in set = %s\n" (show x) (show st)

    data Container a = Container {
                          firstSet  :: MSet a
                        , secondSet :: MSet a
                      }-- deriving (Show)

    instance Show a => Show (Container a) where
      show (Container fSet sSet) = printf "{%-25s %-25s}" (show fSet) (show sSet)

    emptyContainer :: Container Int
    emptyContainer = Container emptyMSet emptyMSet

    addToContainer :: (Show a, Ord a) => a -> Container a -> Container a
    addToContainer x cont
      | sz1 + sz2 == 0            = cont { firstSet  = insertIntoMSet x fSet }
      | sz2 == 0 && x >= mx1      = cont { secondSet = insertIntoMSet x sSet }
      | sz2 == 0 && x <  mx1      = cont { firstSet  = insertIntoMSet x (deleteFromMSet mx1 fSet),
                                           secondSet = insertIntoMSet mx1 sSet }
      | sz1 == sz2 && x <= mn2    = cont { firstSet  = insertIntoMSet x fSet }
      | sz1 == sz2 && x > mn2     = cont { firstSet  = insertIntoMSet mn2 fSet,
                                           secondSet = insertIntoMSet x (deleteFromMSet mn2 sSet) }
      | sz1 == sz2+1 && x >= mx1  = cont { secondSet = insertIntoMSet x sSet}
      | sz1 == sz2+1 && x < mx1   = cont { firstSet  = insertIntoMSet x (deleteFromMSet mx1 fSet),
                                           secondSet = insertIntoMSet mx1 sSet}
      | otherwise                 = error $ printf "Error\nfirstSet = %s\nsecondSet = %s\nmx1 = %s, mn2 = %s, x = %s" (show fSet) (show sSet) (show mx1) (show mn2) (show x)
        where
          sz1 = size. firstSet $ cont
          sz2 = size. secondSet $ cont
          fSet = firstSet cont
          sSet = secondSet cont
          mx1 = MultiSet.findMax. mSet $ fSet
          mn2 = MultiSet.findMin. mSet $ sSet

    getMedian :: (Show a, Ord a) => Container a -> a
    getMedian = MultiSet.findMax. mSet. firstSet

    main :: IO ()
    main = getContents >>= mapM_ (print. getMedian). tail. V.toList. validate. map read. lines

    validate (n:a)
      | n /= len = error $ printf "n = %d, len = %d\n" n len
      | any (\x -> x > 10^9|| x < -n) a = error $ printf "a = %s\n" (show a)
      | otherwise = containers
        where
          len = length a
          vArr = V.fromList a
          containers1 = scanl (flip addToContainer) emptyContainer a
          containers = V.fromList $ emptyContainer : [getContainer i | i <- [1..n]]
          getContainer idx
            | val >= 0 = addToContainer val (containers V.! (idx-1))
            | val < 0  = containers V.! (idx + val)
              where
                val = vArr V.! (idx-1)