Problem:
Multi-Map

For a dictionary $M$ that stores elements formed by a pair of a string key and an integer value, perform a sequence of the following operations. Note that multiple elements can have equivalent keys.

insert($key$, $x$): Insert an element formed by a pair of $key$ and $x$ to $M$.

get($key$): Print all values with the specified $key$.

delete($key$): Delete all elements with the specified $key$.

dump($L$, $R$): Print all elements formed by a pair of the key and the value such that the key is greater than or equal to $L$ and less than or equal to $R$ in lexicographic order.

Input

The input is given in the following format.

$q$
$query_1$
$query_2$
:
$query_q$

Each query $query_i$ is given by

0 $key$ $x$

or

1 $key$

or

2 $key$

or

3 $L$ $R$

where the first digits 0, 1, 2 and 3 represent insert, get, delete and dump operations.

Output

For each get operation, print the corresponding values in the order of insertions.

For each dump operation, print the corresponding elements formed by a pair of the key and the value. For the dump operation, print the elements in ascending order of the keys, in case of a tie, in the order of insertions.

Constraints

$1 \leq q \leq 200,000$

$1 \leq x \leq 1,000,000,000$

$1 \leq $ length of $key$ $ \leq 20$

$key$ consists of lower-case letters

$L \leq R$ in lexicographic order

The total number of elements printed by get operations does not exceed $500,000$

The total number of elements printed by dump operations does not exceed $500,000$

Sample Input 1

10
0 blue 6
0 red 1
0 blue 4
0 white 5
1 red
1 blue
2 red
1 black
1 red
3 w z

Sample Output 1

1
6
4
white 5

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using SB = System.Text.StringBuilder;
//using System.Text.RegularExpressions;
//using System.Globalization;
//using System.Diagnostics;
using static System.Console;
using System.Numerics;
using static System.Math;
using pair = Pair<int, int>;

class Program
{
    static void Main()
    {
        SetOut(new StreamWriter(OpenStandardOutput()) { AutoFlush = false });
        new Program().solve();
        Out.Flush();
    }
    readonly Scanner cin = new Scanner();
    readonly int[] dd = { 0, 1, 0, -1, 0 }; //→↓←↑
    readonly int mod = 1000000007;
    readonly int dom = 998244353;
    bool chmax<T>(ref T a, T b) where T : IComparable<T> { if (a.CompareTo(b) < 0) { a = b; return true; } return false; }
    bool chmin<T>(ref T a, T b) where T : IComparable<T> { if (b.CompareTo(a) < 0) { a = b; return true; } return false; }

    void solve()
    {
        var M = new MultiMap<string, int>();
        int Q = cin.nextint;
        for (int i = 0; i < Q; i++)
        {
            int mode = cin.nextint;
            if (mode == 0)
            {
                M.Add(cin.next, cin.nextint);
            }
            else if (mode == 1)
            {
                M.GetValue(cin.next);
            }
            else if (mode == 2)
            {
                M.Remove(cin.next);
            }
            else if (mode == 3)
            {
                M.GetViewBetween(cin.next, cin.next);
            }
        }
    }

}


class MultiMap<TKey, TValue> where TKey : IComparable<TKey>
{
    SortedSet<TKey> set;
    Dictionary<TKey, List<TValue>> map;
    public MultiMap(IComparer<TKey> comparer, IEqualityComparer<TKey> equalityComparer)
    {
        set = new SortedSet<TKey>(comparer);
        map = new Dictionary<TKey, List<TValue>>(equalityComparer);
    }
    public MultiMap() : this(Comparer<TKey>.Default, EqualityComparer<TKey>.Default) { }

    public void Add(TKey key, TValue value)
    {
        if (!map.ContainsKey(key))
        {
            set.Add(key);
            map[key] = new List<TValue>();
        }
        map[key].Add(value);
    }
    public void GetValue(TKey key)
    {
        if (!map.ContainsKey(key))
        {
            return;
        }
        foreach (var value in map[key])
        {
            WriteLine(value);
        }
    }
    public void Remove(TKey key)
    {
        map.Remove(key);
        set.Remove(key);
    }
    public void GetViewBetween(TKey lowerKey, TKey upperKey)
    {
        foreach (var key in set.GetViewBetween(lowerKey, upperKey))
        {
            foreach (var value in map[key])
            {
                WriteLine($"{key} {value}");
            }
        }
    }
}

static class Ex
{
    public static void join<T>(this IEnumerable<T> values, string sep = " ") => WriteLine(string.Join(sep, values));
    public static string concat<T>(this IEnumerable<T> values) => string.Concat(values);
    public static string reverse(this string s) { var t = s.ToCharArray(); Array.Reverse(t); return t.concat(); }

    public static int lower_bound<T>(this IList<T> arr, T val) where T : IComparable<T>
    {
        int low = 0, high = arr.Count;
        int mid;
        while (low < high)
        {
            mid = ((high - low) >> 1) + low;
            if (arr[mid].CompareTo(val) < 0) low = mid + 1;
            else high = mid;
        }
        return low;
    }
    public static int upper_bound<T>(this IList<T> arr, T val) where T : IComparable<T>
    {
        int low = 0, high = arr.Count;
        int mid;
        while (low < high)
        {
            mid = ((high - low) >> 1) + low;
            if (arr[mid].CompareTo(val) <= 0) low = mid + 1;
            else high = mid;
        }
        return low;
    }
}

class Pair<T, U> : IComparable<Pair<T, U>> where T : IComparable<T> where U : IComparable<U>
{
    public T f; public U s;
    public Pair(T f, U s) { this.f = f; this.s = s; }
    public int CompareTo(Pair<T, U> a) => f.CompareTo(a.f) != 0 ? f.CompareTo(a.f) : s.CompareTo(a.s);
    public override string ToString() => $"{f} {s}";
}

class Scanner
{
    string[] s; int i;
    readonly char[] cs = new char[] { ' ' };
    public Scanner() { s = new string[0]; i = 0; }
    public string[] scan => ReadLine().Split();
    public int[] scanint => Array.ConvertAll(scan, int.Parse);
    public long[] scanlong => Array.ConvertAll(scan, long.Parse);
    public double[] scandouble => Array.ConvertAll(scan, double.Parse);
    public string next
    {
        get
        {
            if (i < s.Length) return s[i++];
            string st = ReadLine();
            while (st == "") st = ReadLine();
            s = st.Split(cs, StringSplitOptions.RemoveEmptyEntries);
            i = 0;
            return next;
        }
    }
    public int nextint => int.Parse(next);
    public long nextlong => long.Parse(next);
    public double nextdouble => double.Parse(next);
}