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: Time Limit Exceeded)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Globalization;
using System.Diagnostics;



class Myon
{
    public Myon() { }
    public static int Main()
    {
        new Myon().calc();
        return 0;
    }
    

    Scanner cin;

    int MAX = 999999;
    
    void calc()
    {
        cin = new Scanner();
        int N = cin.nextInt();
        int[] A = cin.ArrayInt(N, -1);

        seg = new Segtree[2];
        seg[0] = new Segtree((N + 1) / 2);
        seg[1] = new Segtree((N + 1) / 2);

        prePos = new int[N];
        for (int i = 0; i < A.Length; i++)
        {
            seg[i % 2].add(i / 2, i / 2 + 1, MAX - A[i]);
            prePos[A[i]] = i;
        }
        a = new List<int>();
        b = new List<int>();
        h = new Heap<long>();
        next = new List<int>[N];
        nowID = 0;

        dfs(0, N, -1);

        h.push(hash(a[0], b[0], 0));

        List<long> ansArray = new List<long>();
        while (h.top != null)
        {
            var nextNode = h.pop();
            long anum = (nextNode >> 40);
            long bnum = (nextNode >> 20) & 0xFFFFF;
            ansArray.Add(MAX - anum + 1);
            ansArray.Add(MAX - bnum + 1);
            int id = (int)(nextNode & 0xFFFFF);
            foreach (var item in next[id])
            {
                h.push(hash(a[item], b[item], item));
            }
        }
        Console.WriteLine(string.Join(" ", ansArray));
    }

    List<int> a;
    List<int> b;
    List<int>[] next;
    Heap<long> h;
    int nowID;

    int[] prePos;
    Segtree[] seg;

    long hash(long a, long b, long c)
    {
        return (a << 40) + (b << 20) + c;
    }

    void dfs(int l, int r, int preID)
    {
        if (r - l <= 0) return;
        int lid = l % 2;
        int lpos = l / 2;
        int rid = 1 - lid;
        int rpos = (r + 1 - l%2) / 2;

        int left = seg[lid].getMax(lpos, rpos);
        int leftPos = prePos[MAX - left];

        int right = seg[rid].getMax((leftPos + 1) / 2, (r + l % 2) / 2);
        int rightPos = prePos[MAX - right];

        a.Add(left);
        b.Add(right);
        int nid = nowID;
        next[nowID] = new List<int>();
        if(preID != -1)
        {
            next[preID].Add(nid);
        }
        nowID++;
        dfs(l, leftPos, nid);
        dfs(leftPos + 1, rightPos, nid);
        dfs(rightPos + 1, r, nid);
    }


    class Heap<T> where T : IComparable
    {
        public HeapNode<T> top;

        public Heap() { }

        public void push(T val)
        {
            top = HeapNode<T>.meld(top, new HeapNode<T>(val));
        }

        public T pop()
        {
            T ret = top.val;
            top = HeapNode<T>.meld(top.r, top.l);
            return ret;
        }

        public void merge(Heap<T> h2)
        {
            top = HeapNode<T>.meld(top, h2.top);
        }

        public class HeapNode<NT> where NT : IComparable
        {
            public HeapNode<NT> l, r;
            public NT val;

            public HeapNode(NT _val)
            {
                val = _val;
            }

            public static HeapNode<NT> meld(HeapNode<NT> a, HeapNode<NT> b)
            {
                if (a == null) return b;
                if (b == null) return a;
                if (a.val.CompareTo(b.val) < 0)
                {
                    HeapNode<NT> temp = a;
                    a = b;
                    b = temp;
                }
                a.r = meld(a.r, b);
                HeapNode<NT> temph = a.l;
                a.l = a.r;
                a.r = temph;
                return a;
            }
        }
    }


    class Segtree
    {
        int N;
        int[] L;
        int[] R;
        int[] M;
        int[,] np;
        bool[] lazyFlag;

        public Segtree(int _N)
        {
            N = 1;
            while (N < _N) N *= 2;

            L = new int[N * 2];
            R = new int[N * 2];
            M = new int[N * 2];
            np = new int[N * 2, 2];
            lazyFlag = new bool[N * 2];
            L[0] = 0;
            R[0] = N;
            M[0] = N / 2;

            for (int i = 0; i < N - 1; i++)
            {
                np[i, 0] = i * 2 + 1;
                np[i, 1] = i * 2 + 2;
                L[np[i, 0]] = L[i];
                R[np[i, 0]] = L[np[i, 1]] = (L[i] + R[i]) / 2;
                R[np[i, 1]] = R[i];
            }
            for (int i = 0; i < N; i++)
            {
                M[i] = (L[i] + R[i]) / 2;
            }
            init();
        }

        int[] sum;
        int[] lazyadd;
        int[] max;

        void init()
        {
            sum = new int[2 * N];
            lazyadd = new int[2 * N];
            max = new int[2 * N];
        }

        //遅延評価する分を伝播させる
        void lazy(int p)
        {
            if (!lazyFlag[p]) return;
            lazyFlag[p] = false;

            add(L[p], M[p], lazyadd[p], np[p, 0]);
            add(M[p], R[p], lazyadd[p], np[p, 1]);

            lazyadd[p] = 0;
        }

        //親の更新処理
        public void merge(int p)
        {
            max[p] = Math.Max(max[np[p, 0]], max[np[p, 1]]);
            sum[p] = sum[np[p, 0]] + sum[np[p, 1]];
        }

        public int getSum(int l, int r, int p = 0)
        {
            if (l == L[p] && r == R[p])
            {
                //一致時処理
                return sum[p];
            }
            else
            {
                //遅延評価分の伝播
                lazy(p);

                //子の集計
                int ans = 0;
                if (l < M[p]) ans += getSum(l, Math.Min(M[p], r), np[p, 0]);
                if (r > M[p]) ans += getSum(Math.Max(l, M[p]), r, np[p, 1]);
                return ans;
            }
        }

        public int getMax(int l, int r, int p = 0)
        {
            if (l == L[p] && r == R[p])
            {
                //一致時処理
                return max[p];
            }
            else
            {
                //遅延評価分の伝播
                lazy(p);

                //子の集計
                int ans = 0;
                if (l < M[p]) ans = Math.Max(ans, getMax(l, Math.Min(M[p], r), np[p, 0]));
                if (r > M[p]) ans = Math.Max(ans, getMax(Math.Max(l, M[p]), r, np[p, 1]));

                return ans;
            }
        }

        public void add(int l, int r, int v, int p = 0)
        {
            if (l == L[p] && r == R[p])
            {
                //一致時処理
                max[p] += v;
                sum[p] += v;
                lazyadd[p] += v;
                lazyFlag[p] = true;
            }
            else
            {
                //遅延評価分の伝播
                lazy(p);
                //子の更新
                if (l < M[p]) add(l, Math.Min(M[p], r), v, np[p, 0]);
                if (r > M[p]) add(Math.Max(l, M[p]), r, v, np[p, 1]);
                //親の更新
                merge(p);

            }
        }
    }

}


class Scanner
{
    string[] s;
    int i;

    char[] cs = new char[] { ' ' };

    public Scanner()
    {
        s = new string[0];
        i = 0;
    }

    public string next()
    {
        if (i < s.Length) return s[i++];
        string st = Console.ReadLine();
        while (st == "") st = Console.ReadLine();
        s = st.Split(cs, StringSplitOptions.RemoveEmptyEntries);
        if (s.Length == 0) return next();
        i = 0;
        return s[i++];
    }

    public int nextInt()
    {
        return int.Parse(next());
    }
    public int[] ArrayInt(int N, int add = 0)
    {
        int[] Array = new int[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = nextInt() + add;
        }
        return Array;
    }

    public long nextLong()
    {
        return long.Parse(next());
    }

    public long[] ArrayLong(int N, long add = 0)
    {
        long[] Array = new long[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = nextLong() + add;
        }
        return Array;
    }

    public double nextDouble()
    {
        return double.Parse(next());
    }


    public double[] ArrayDouble(int N, double add = 0)
    {
        double[] Array = new double[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = nextDouble() + add;
        }
        return Array;
    }
}