#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <tuple>
#include <functional>

using namespace std;

template< typename Monoid >
struct SegmentTree {
    using F = function< Monoid(Monoid, Monoid) >;

    int sz;
    vector< Monoid > seg;

    const F f;
    const Monoid M1;

    SegmentTree(int n, const F f, const Monoid &M1) : f(f), M1(M1) {
        sz = 1;
        while(sz < n) sz <<= 1;
        seg.assign(2 * sz, M1);
    }

    void set(int k, const Monoid &x) {
        seg[k + sz] = x;
    }

    void build() {
        for(int k = sz - 1; k > 0; k--) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    void update(int k, const Monoid &x) {
        k += sz;
        seg[k] = x;
        while(k >>= 1) {
            seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
        }
    }

    Monoid query(int a, int b) {
        Monoid L = M1, R = M1;
        for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if(a & 1) L = f(L, seg[a++]);
            if(b & 1) R = f(seg[--b], R);
        }
        return f(L, R);
    }

    Monoid operator[](const int &k) const {
        return seg[k + sz];
    }

    template< typename C >
    int find_subtree(int a, const C &check, Monoid &M, bool type) {
        while(a < sz) {
            Monoid nxt = type ? f(seg[2 * a + type], M) : f(M, seg[2 * a + type]);
            if(check(nxt)) a = 2 * a + type;
            else M = nxt, a = 2 * a + 1 - type;
        }
        return a - sz;
    }


    template< typename C >
    int find_first(int a, const C &check) {
        Monoid L = M1;
        if(a <= 0) {
            if(check(f(L, seg[1]))) return find_subtree(1, check, L, false);
            return -1;
        }
        int b = sz;
        for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
            if(a & 1) {
                Monoid nxt = f(L, seg[a]);
                if(check(nxt)) return find_subtree(a, check, L, false);
                L = nxt;
                ++a;
            }
        }
        return -1;
    }

    template< typename C >
    int find_last(int b, const C &check) {
        Monoid R = M1;
        if(b >= sz) {
            if(check(f(seg[1], R))) return find_subtree(1, check, R, true);
            return -1;
        }
        int a = sz;
        for(b += sz; a < b; a >>= 1, b >>= 1) {
            if(b & 1) {
                Monoid nxt = f(seg[--b], R);
                if(check(nxt)) return find_subtree(b, check, R, true);
                R = nxt;
            }
        }
        return -1;
    }
};


int main(void) {
    int N;
    cin >> N;
    vector<int> index(N+1, -1);
    SegmentTree<int> seg_even(N, [](int a, int b) {return min(a, b);}, numeric_limits<int>::max());
    SegmentTree<int> seg_odd(N, [](int a, int b) {return min(a, b);}, numeric_limits<int>::max());
    for(int i=0; i<N; i++) {
        int p;
        cin >> p;
        if(i%2) {
            seg_odd.update(i, p);
        } else {
            seg_even.update(i, p);
        }
        index[p] = i;
    }

    vector<int> ans;
    priority_queue< tuple<int,int,int,int,int,int>, vector<tuple<int,int,int,int,int,int>>, greater<tuple<int,int,int,int,int,int>> > pq;

    auto f = [&](int l, int r) {
        if(l >= r) return;
        if(l%2) {
            int min_l = seg_odd.query(l, r);
            int min_r = seg_even.query(index[min_l], r);
            pq.emplace(min_l, min_r, index[min_l], index[min_r], l, r);
        } else {
            int min_l = seg_even.query(l, r);
            int min_r = seg_odd.query(index[min_l], r);
            pq.emplace(min_l, min_r, index[min_l], index[min_r], l, r);
        }
        return;
    };

    f(0, N);
    while(!pq.empty()) {
        auto top = pq.top();
        pq.pop();
        ans.emplace_back(get<0>(top));
        ans.emplace_back(get<1>(top));
        f(get<4>(top), get<2>(top));
        f(get<2>(top)+1, get<3>(top));
        f(get<3>(top)+1, get<5>(top));
    }

    for(auto e: ans) {
        cout << e << ' ';
    }
    cout << endl;
    return 0;
}


Measured memory_bytes: 9140