はまやんはまやんはまやん

hamayanhamayan's blog

Young Maids [AtCoder Regular Contest 080 E]

http://arc080.contest.atcoder.jp/tasks/arc080_c

解説

http://arc080.contest.atcoder.jp/submissions/1491823

問題のアルゴリズムでは、配列を2つずつ後ろから決めていくが、先頭から2つずつ決めていくこととする。
区間[L,R]から2つ選ぶことを考えるが、以下のように選択する。

1. 区間の先頭から数えて奇数番目の要素の最小要素を取り出す
2. 1番目の要素以降で、かつ、区間の先頭から数えて偶数番目の要素の最小要素を取り出す

これで最適な2つを選択することができる。
選択1での要素番号をA, 選択2での要素番号をBとすると、[L,A), (A,B), (B,C]の三区間再帰的にまた選択できる。
選択するとその先が選択できるこの構造は有向木の構造となる。
これを再帰でまず構築する。
 
奇数番目、偶数番目の最小要素を取り出すには、奇数番目用・偶数番目用のセグメントツリーを用意し、適宜切り替えることで実現できる。
 
遷移先は互いに独立であれば、順番は入れ替えることができるため、
priority_queueを使って小さいペアから順に選択して決定していけば、最適な答えが導ける。

int N, P[201010];
SegTree<pair<int, int>, 1 << 18> st1, st2;
//---------------------------------------------------------------------------------------------------
vector<int> E[201010];
int PA[201010];
void dfs(int L, int R, int pa = 0) {
    pair<int, int> mi1, mi2;
 
    if (L + 1 == R) {
        E[pa].push_back(P[L]);
        PA[P[L]] = P[R];
        return;
    }
 
    if (L % 2 == 0) {
        mi1 = st1.get(L, R - 1);
        mi2 = st2.get(mi1.second + 1, R);
    } else {
        mi1 = st2.get(L, R - 1);
        mi2 = st1.get(mi1.second + 1, R);
    }
 
    int A = mi1.second;
    int B = mi2.second;
 
    E[pa].push_back(mi1.first);
    PA[mi1.first] = mi2.first;
 
    if (L != A) dfs(L, A - 1, mi1.first);
    if (A + 1 != B) dfs(A + 1, B - 1, mi1.first);
    if (R != B) dfs(B + 1, R, mi1.first);
}
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> N; rep(i, 0, N) cin >> P[i];
 
    rep(i, 0, N) {
        if (i % 2 == 0) st1.update(i, { P[i], i });
        else st2.update(i, { P[i], i });
    }
 
    dfs(0, N - 1);
 
    vector<int> ans;
    priority_queue<int> que;
    que.push(-E[0].front());
 
    while (!que.empty()) {
        int q = -que.top(); que.pop();
 
        ans.push_back(q);
        ans.push_back(PA[q]);
 
        fore(i, E[q]) que.push(-i);
    }
 
    rep(i, 0, ans.size()) {
        if (i) printf(" ");
        printf("%d", ans[i]);
    }
    printf("\n");
}