C - Sugoroku Destination Editorial by MMNMM


公式解説では、移動の性質に注目することで「\(10 ^ {100}\) 回操作」を「止まるまで操作」に言い換えましたが、性質に関する考察をすることなく \(10 ^ {100}\) 回移動した結果を求めることができます。

\(i=1,2,\ldots,N\) に対して同時に \(A _ i\leftarrow{A _ {A _ {A _ {A _ {A _ {A _ {A _ {A _ {A _ {A _ i}}}}}}}}}}\) と更新を行うことで、各 \(i\) から \(10\) 回操作を行ったあとにいるマスを求めることができます。 この更新を \(100\) 回行うことで、各 \(i\) から \(10 ^ {100}\) 回操作を行ったあとにいるマスを十分高速に求めることができます。

実装例は以下のようになります。

#include <iostream>
#include <vector>
#include <ranges>

int main() {
    using namespace std;
    int N;
    cin >> N;

    vector<int> A(N);
    for (auto&& a : A) {
        cin >> a;
        --a; // 0-indexed にしておく
    }

    // 10 倍するのを 100 回繰り返す
    for (int i = 0; i < 100; ++i) {
        vector<int> tmp(N);
        for (int j : views::iota(0, N)) {
            tmp[j] = A[A[A[A[A[A[A[A[A[A[j]]]]]]]]]];
        }
        A = tmp;
    }

    // 1-indexed に直して出力
    for (const auto a : A) {
        cout << a + 1 << " ";
    }
    cout << endl;

    return 0;
}

posted:
last update: