「転倒数 = N-1 で Yes とする」という嘘解法が大量に通ったらしい
問題概要
の順列 が与えられる。
- と を swap する
- と を swap する
- ...
- と を swap する
という 種類の操作を、それぞれちょうど 1 回ずつ行う必要がある。その結果がソートされた状態にすることが可能かどうか判定し、可能ならば具体的な手順を提示せよ。
制約
考えたこと
この手の問題は、最大値や最小値に着目するといいことがあったりする。ひとつ言えるのは、1 は最終的に左端に来なければならないので、仮に
となる箇所があったならば、 に対するどの操作よりも、 に対する操作を行う必要がある。同様に考えて
「操作 をこの順に行うとしてしまってよい」
ということが言える。このとき、 については二度と値を変更できないので、これらが を満たさない場合は false となる。
これらを実施した結果、今度は に対して再帰的に同様に処理していけば OK。このとき、
- の最小値
- 最小となる index
を高速に求めたい。コンテスト中は RMQ を使って通した。しかしながら最小値が であることはわかっているので、 の逆行列 を予め計算しておけば、P'[i] を参照するだけでよかった。
コード
計算量は となる。
#include <bits/stdc++.h> using namespace std; int N; vector<int> P, iP; bool solve(int left, vector<int> &res) { if (res.size() == N-1) { for (int i = 0; i < N; ++i) if (P[i] != i) return false; return true; } if (iP[left] <= left) return false; for (int j = iP[left]; j > left; --j) { res.push_back(j); swap(P[j], P[j-1]); } for (int j = left; j < iP[left]; ++j) if (P[j] != j) return false; return solve(iP[left], res); } int main() { cin >> N; P.resize(N), iP.resize(N); for (int i = 0; i < N; ++i) cin >> P[i], --P[i], iP[P[i]] = i; vector<int> res; if (!solve(0, res)) cout << -1 << endl; else for (auto v : res) cout << v << endl; }