hogecoder

つたじろう(Tsuta_J) 競技プログラミングの記録

CS Academy: Sorting Steps

公式の解説が鮮やかだったけど、英語がちょっと読みにくかったので日本語解説を作ってみたくなりました。

問題概要

原文 → CS Academy

長さ  N (1 \leq N \leq 10^{5}) の配列をバブルソートすることを考える (どんな実装かは、原文にサンプルコードがあるのでそちらを参照してください)。

ソートが完了した時の、"steps" の値を出力せよ。

解説

なんとなくセグ木とかを使いたい気持ちになりますが、公式の解説を見るとソートをするだけで解いているのでビックリです。どうやれば解けるのか、詳しく見ていきましょう。なお、説明のため、 A_{i} := 配列の  i 番目の値 とします。 (ここからの内容はほぼ公式解説と同じです)

まず、 C_{i} :=  i 番目の要素より左にあり、かつ値が  A_{i} よりも大きい要素の個数 と定義します。もしも全ての  i について  C_{i} = 0 ならば、既にソート済みです。

ここで重要な考察として、バブルソート 1 ステップ進めると、 C_{i} \gt 0 を満たす全ての  C の値がそれぞれ  1 減少します。 なぜなら、それを満たす要素それぞれについて、自分より左にある要素の中で最大の要素とスワップされ、自分より左にあって自分より大きい要素の個数が  1 個減るからです。全ての  C の値が  0 (= ソート済み) になるために必要なステップ数が  \max(C) + 1 回であることは明らかなので、あとは  \max(C) を求められれば OK です。

全ての  C を求めるのであれば前述の通りセグ木等を使う必要がありますが、今回必要な値は  \max だけなので、もっとシンプルに解くことができます。

まず、どのような要素が  \max(C) の候補になりえる・またはなりえないかを考えます。これについて、 ソート前配列の  i 番目の要素について、その右に自分より小さい要素があるならば、 C_{i} \max になることはない という重要な考察ができます。

これを簡単に証明します。 i \lt j であって  A_{i} \gt A_{j} である組  (i, j) を考えます。このとき、 C_{j} の計算において  i 番目の要素を考慮すると  C_{i} \lt C_{j} が成り立つことがわかります。これにより、自分より小さい要素が自分より右にあるならば  \max(C) の候補になりえないことが示せました。 (個人的にここの部分が一番巧妙だと思います。)

つまり、 \max(C) の候補になる要素は、 自分より小さい要素全てが自分より左の位置にあること を満たす必要があります。そしてそのような候補について、 C は (ソート済みの状態であるときのインデックス) - (ソート前の状態であるときのインデックス) で計算できます。よって、元のインデックスの情報を保持しながら配列をソートして、前述の計算の  \max を取れば良いです。

ソースコード

ほぼ Writer 解と同じですが・・・。

signed main() {
    int N; cin >> N;
    vector<pii> v(N);
    rep(i,0,N) {
        int p; cin >> p;
        v[i] = make_pair(p, i);
    }
    sort(v.begin(), v.end());

    int ans = 0;
    rep(i,0,N) {
        chmax(ans, v[i].second - i);
    }
    cout << ans + 1 << endl;
    return 0;
}

こういうシンプルな考察ができる頭になりたい。