F - Simultaneous Swap Editorial by MMNMM

より高速な解法

この問題は、全体で線形時間で解くこともできます。

長さ \(N\) の配列を持ち、それぞれの列に \(i\) が何回出現したかを管理することで、「それぞれの列に出現する値が多重集合として等しいか」や「同じ値が重複して出現するか」を \(\Theta(N)\) 時間で判定できます。

同じ値が重複しないとき、与えられた列は \(\{1,\ldots,N\}\) の置換です。 置換 \(i\mapsto P _ i\) が偶置換であるかは、\(P ^ {-1} _ {P _ i}=i\) なる逆置換 \(P ^ {-1}\) を持ちながらソートを行うことで \(\Theta(N)\) 時間で判定できます。 (置換を disjoint な巡回に分解することでも、線形時間で置換の偶奇を判定することができます。)

よって、この問題を \(\Theta(N)\) 時間で解くことができました。

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

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    using namespace std;

    unsigned N;
    cin >> N;
    vector<unsigned> A(N), B(N);
    for (auto &&a : A)
        cin >> a;
    for (auto &&b : B)
        cin >> b;

    // count_A[i] = (i が A に出現した回数)
    // count_B[i] = (i が B に出現した回数)
    vector<unsigned> count_A(N), count_B(N);
    for (auto &&a : A)
        ++count_A[--a];
    for (auto &&b : B)
        ++count_B[--b];

    // 多重集合として一致していないなら No
    if (count_A != count_B) {
        cout << "No" << endl;
        return 0;
    }
    // そうでなく、同じ要素が複数回出現していたら Yes
    if (*max_element(begin(count_A), end(count_A)) >= 2) {
        cout << "Yes" << endl;
        return 0;
    }

    // P[i] = B[A[i]] が偶置換 <=> A[i] と B[i] の置換の偶奇は等しい
    vector<unsigned> P(N), inv_P(N);
    transform(begin(A), end(A), begin(P), [&B](auto i) { return B[i]; });
    for (unsigned i{}; i < N; ++i)
        inv_P[P[i]] = i;

    bool ans{true};
    for (unsigned i{}; i < N; ++i)
        if (i != P[i]) {
            // P[i] と i を swap
            swap(inv_P[i], inv_P[P[i]]);
            swap(P[i], P[inv_P[P[i]]]);
            ans ^= true;
        }
    cout << (ans ? "Yes" : "No") << endl;

    return 0;
}

posted:
last update: