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"); }