Problem:
Score : 800 points

Problem Statement

Let N be a positive even number.

We have a permutation of (1, 2, ..., N), p = (p_1, p_2, ..., p_N).
Snuke is constructing another permutation of (1, 2, ..., N), q, following the procedure below.

First, let q be an empty sequence.
Then, perform the following operation until p becomes empty:

Select two adjacent elements in p, and call them x and y in order. Remove x and y from p (reducing the length of p by 2), and insert x and y, preserving the original order, at the beginning of q.

When p becomes empty, q will be a permutation of (1, 2, ..., N).

Find the lexicographically smallest permutation that can be obtained as q.

Constraints

N is an even number.

2 ≤ N ≤ 2 × 10^5

p is a permutation of (1, 2, ..., N).

Input

Input is given from Standard Input in the following format:

N
p_1 p_2 ... p_N

Output

Print the lexicographically smallest permutation, with spaces in between.

Sample Input 1

4
3 2 4 1

Sample Output 1

3 1 2 4

The solution above is obtained as follows:

p

q

(3, 2, 4, 1)

()

↓

↓

(3, 1)

(2, 4)

↓

↓

()

(3, 1, 2, 4)

Sample Input 2

2
1 2

Sample Output 2

1 2

Sample Input 3

8
4 6 3 2 8 5 7 1

Sample Output 3

3 1 2 7 4 6 8 5

The solution above is obtained as follows:

p

q

(4, 6, 3, 2, 8, 5, 7, 1)

()

↓

↓

(4, 6, 3, 2, 7, 1)

(8, 5)

↓

↓

(3, 2, 7, 1)

(4, 6, 8, 5)

↓

↓

(3, 1)

(2, 7, 4, 6, 8, 5)

↓

↓

()

(3, 1, 2, 7, 4, 6, 8, 5)

Solution in C# (Mono 4.6.2.0) (judge verdict: Accepted)

using System;
using System.Collections.Generic;


class SegTree<T>
{
    private readonly T[] Array;
    private readonly Comparison<T> Compare;
    private readonly int N;
    private readonly T Max;

    public SegTree(int size, T max) : this(size, Comparer<T>.Default, max)
    {
    }

    public SegTree(int size, IComparer<T> comparer, T max) : this(size, comparer.Compare, max)
    {
    }

    public SegTree(int size, Comparison<T> comparison, T max)
    {
        Max = max;
        N = 1;
        while (N < size)
        {
            N *= 2;
        }

        Compare = comparison;
        Array = new T[2 * N];
        for (int i = 1; i < 2 * N; i++)
        {
            Array[i] = max;
        }
    }

    new public T this[int i]
    {
        get { return Array[i + N]; }
        set { Update(value, i); }
    }


    private T Min(T x, T y)
    {
        return Compare(x, y) < 0 ? x : y;
    }

    public void Update(T item, int index)
    {
        index += N;
        Array[index] = item;
        while (index > 1)
        {
            index /= 2;
            Array[index] = Min(Array[index * 2], Array[index * 2 + 1]);
        }
    }

    private T Q(int left, int right, int k, int l, int r)
    {
        if (left <= l && r <= right)
        {
            return Array[k];
        }

        if (r <= left || right <= l)
        {
            return Max;
        }

        return Min(Q(left, right, k * 2, l, (l + r) / 2), Q(left, right, k * 2 + 1, (l + r) / 2, r));
    }

    public T Query(int left, int right)
    {
        return Q(left, right, 1, 0, N);
    }
}

#region PriorityQueue

/// <summary>
/// 指定した型のインスタンスを最も価値が低い順に取り出すことが可能な可変サイズのコレクションを表します．
/// </summary>
/// <typeparam name="T">優先度付きキュー内の要素の型を指定します．</typeparam>
/// <remarks>内部的にはバイナリヒープによって実装されています．</remarks>
public class PriorityQueue<T>
{
    readonly List<T> heap = new List<T>();
    readonly Comparison<T> cmp;

    /// <summary>
    /// デフォルトの比較子を使用してインスタンスを初期化します．
    /// </summary>
    /// <remarks>この操作は O(1) で実行されます．</remarks>
    public PriorityQueue()
    {
        cmp = Comparer<T>.Default.Compare;
    }

    /// <summary>
    /// デリゲートで表されるような比較関数を使用してインスタンスを初期化します．
    /// </summary>
    /// <param name="comparison"></param>
    /// <remarks>この操作は O(1) で実行されます．</remarks>
    public PriorityQueue(Comparison<T> comparison)
    {
        cmp = comparison;
    }

    /// <summary>
    /// 指定された比較子を使用してインスタンスを初期化します．
    /// </summary>
    /// <param name="comparer"></param>
    /// <remarks>この操作は O(1) で実行されます．</remarks>
    public PriorityQueue(IComparer<T> comparer)
    {
        cmp = comparer.Compare;
    }

    /// <summary>
    /// 優先度付きキューに要素を追加します．
    /// </summary>
    /// <param name="item">優先度付きキューに追加される要素</param>
    /// <remarks>最悪計算量 O(log N) で実行されます．</remarks>
    public void Enqueue(T item)
    {
        var pos = heap.Count;
        heap.Add(item);
        while (pos > 0)
        {
            var par = (pos - 1) / 2;
            if (cmp(heap[par], item) <= 0)
                break;
            heap[pos] = heap[par];
            pos = par;
        }

        heap[pos] = item;
    }

    /// <summary>
    /// 優先度付きキューから最も価値が低い要素を削除し，返します．
    /// </summary>
    /// <returns>優先度付きキューから削除された要素．</returns>
    /// <remarks>最悪計算量 O(log N) で実行されます．</remarks>
    public T Dequeue()
    {
        var ret = heap[0];
        var pos = 0;
        var x = heap[heap.Count - 1];

        while (pos * 2 + 1 < heap.Count - 1)
        {
            var lch = pos * 2 + 1;
            var rch = pos * 2 + 2;
            if (rch < heap.Count - 1 && cmp(heap[rch], heap[lch]) < 0) lch = rch;
            if (cmp(heap[lch], x) >= 0)
                break;
            heap[pos] = heap[lch];
            pos = lch;
        }

        heap[pos] = x;
        heap.RemoveAt(heap.Count - 1);
        return ret;
    }

    /// <summary>
    ///  優先度付きキューに含まれる最も価値が低い要素を削除せずに返します．
    /// </summary>
    /// <returns>優先度付きキューに含まれる最も価値が低い要素．</returns>
    /// <remarks>この操作は O(1) で実行されます．</remarks>
    public T Peek()
    {
        return heap[0];
    }

    /// <summary>
    /// 優先度付きキュー内の要素の数を取得します．
    /// </summary>
    /// <returns>優先度付キュー内にある要素の数</returns>
    /// <remarks>最悪計算量 O(1) で実行されます．</remarks>
    public int Count
    {
        get { return heap.Count; }
    }

    /// <summary>
    /// 優先度付きキュー内に要素が存在するかどうかを O(1) で判定します．
    /// </summary>
    /// <returns>優先度付キュー内にある要素が存在するならば true，そうでなければ　false．</returns>
    /// <remarks>この操作は O(1) で実行されます．</remarks>
    public bool Any()
    {
        return heap.Count > 0;
    }

    /// <summary>
    /// 優先度付きキューに含まれる要素を昇順に並べて返します．
    /// </summary>
    /// <remarks>この操作は計算量 O(N log N)で実行されます．</remarks>
    public T[] Items
    {
        get
        {
            var ret = heap.ToArray();
            Array.Sort(ret, cmp);
            return ret;
        }
    }
}

#endregion

public struct Range
{
    public int Left, Right;

    public S X, Y;

    public Range(int l, int r, S x, S y)
    {
        Left = l;
        Right = r;
        X = x;
        Y = y;
    }
}

public struct S
{
    public int N;
    public int Index;

    public S(int n, int i)
    {
        N = n;
        Index = i;
    }
}

public class Program
{
    private int N;
    private int[] P;

    // 奇数　偶数
    private SegTree<S> Odd, Even;

    private PriorityQueue<Range> PQ;

    public void Solve()
    {
        var sc = new Scanner();
        N = sc.NextInt();
        P = sc.IntArray();

        Odd = new SegTree<S>(N, (a, b) => a.N.CompareTo(b.N), new S(int.MaxValue, int.MaxValue));
        Even = new SegTree<S>(N, (a, b) => a.N.CompareTo(b.N), new S(int.MaxValue, int.MaxValue));

        for (int i = 0; i < N; i++)
        {
            if (i % 2 == 0)
            {
                Even[i] = new S(P[i], i);
            }
            else
            {
                Odd[i] = new S(P[i], i);
            }
        }

        PQ = new PriorityQueue<Range>((a, b) => a.X.N.CompareTo(b.X.N));

        Range all;
        all.Left = 0;
        all.Right = N;
        var e = Even.Query(0, N);

        all.X = e;
        var o = Odd.Query(e.Index + 1, N);
        all.Y = o;
        PQ.Enqueue(all);

        List<int> ans = new List<int>();
        while (PQ.Count > 0)
        {
            var r = PQ.Dequeue();
            ans.Add(r.X.N);
            ans.Add(r.Y.N);

            // [r.Left r.X.Index) [r.X.Index + 1, r.Y.Index) [r.Y.Index+1, r.Right)
            if (r.X.Index  - r.Left > 0)
            {
                Range a = CalcXY(r.Left, r.X.Index);
                PQ.Enqueue(a);
            }

            if (r.Y.Index - (r.X.Index + 1) > 0)
            {
                Range b = CalcXY(r.X.Index + 1, r.Y.Index );
                PQ.Enqueue(b);
            }
            
            if (r.Right - (r.Y.Index + 1) > 0)
            {
                Range c = CalcXY(r.Y.Index + 1, r.Right);
                PQ.Enqueue(c);
            }
        }
        
        Console.WriteLine(string.Join(" ",ans));
    }

    public Range CalcXY(int left, int right)
    {
        Range res;
        res.Left = left;
        res.Right = right;
        if (left % 2 == 0)
        {
            res.X = Even.Query(left, right);
            res.Y = Odd.Query(res.X.Index, right);
        }
        else
        {
            res.X = Odd.Query(left, right);
            res.Y = Even.Query(res.X.Index, right);
        }

        return res;
    }


    public static void Main(string[] args)
    {
        new Program().Solve();
    }
}

class Scanner
{
    public Scanner()
    {
        _pos = 0;
        _line = new string[0];
    }

    const char Separator = ' ';
    private int _pos;
    private string[] _line;

    #region スペース区切りで取得

    public string Next()
    {
        if (_pos >= _line.Length)
        {
            _line = Console.ReadLine().Split(Separator);
            _pos = 0;
        }

        return _line[_pos++];
    }

    public int NextInt()
    {
        return int.Parse(Next());
    }

    public long NextLong()
    {
        return long.Parse(Next());
    }

    public double NextDouble()
    {
        return double.Parse(Next());
    }

    #endregion

    #region 型変換

    private int[] ToIntArray(string[] array)
    {
        var result = new int[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            result[i] = int.Parse(array[i]);
        }

        return result;
    }

    private long[] ToLongArray(string[] array)
    {
        var result = new long[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            result[i] = long.Parse(array[i]);
        }

        return result;
    }

    private double[] ToDoubleArray(string[] array)
    {
        var result = new double[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            result[i] = double.Parse(array[i]);
        }

        return result;
    }

    #endregion

    #region 配列取得

    public string[] Array()
    {
        if (_pos >= _line.Length)
            _line = Console.ReadLine().Split(Separator);

        _pos = _line.Length;
        return _line;
    }

    public int[] IntArray()
    {
        return ToIntArray(Array());
    }

    public long[] LongArray()
    {
        return ToLongArray(Array());
    }

    public double[] DoubleArray()
    {
        return ToDoubleArray(Array());
    }

    #endregion
}