ABC302G Sort from 1 to 4 [关键性质题]

Description

给定一个长度为 \(N\) 的序列,其中每个元素都是介于 \(1\)\(4\) 之间的整数。

可以进行以下操作任意次(可能为零次):

  • 选择一对整数 \((i, j)\),其中 \(1≤i<j≤N\),并交换 \(A_i\)\(A_j\)

输出使序列 \(A\) 变为非递减序列所需的最小操作次数。

Solution

对于这种交换数字,最小操作数是序列相等,或者什么单调性的题,可以优先想到逆序对和建图。

设原序列为 \(A=(A_1, A_2, ..., A_N)\),排序后为 \(B=(B_1, B_2, ..., B_N)\)

根据观察,若 \(A_i\ne B_i\),则我们用它们代表的数字建边 \(A_i\to B_i\)

由于每个点的出度和入度都相等,所以最终会形成若干个环,且根据 \(A_i\in [1,4]\) 这重要性质,可知环长不超过 \(4\)

考虑一个长度为 \(i\) 的环,要把它变为合法,需要 \(i-1\) 步,因此我们优先把长度为 \(2\) 的环交换,接着是 \(3\),最后是 \(4\),直接暴力枚举计算贡献即可。

Code

const int N = 2e5 + 5;

int n, a[N], b[N], e[5][5];

void Solve(){
    cin >> n;
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        b[i] = a[i];
    }
    sort(b + 1, b + 1 + n);
    for(int i = 1; i <= n; i++){
        if(a[i] != b[i]) e[a[i]][b[i]]++;
    }

    ll ans = 0;

    for(int i = 1; i <= 4; i++){
        for(int j = 1; j <= 4; j++){
            if(e[i][j] && e[j][i]){
                ll mn = min(e[i][j], e[j][i]);
                ans += mn;
                e[i][j] -= mn;
                e[j][i] -= mn;
            }
        }
    }

    for(int i = 1; i <= 4; i++){
        for(int j = 1; j <= 4; j++){
            for(int k = 1; k <= 4; k++){
                if(i == j || j == k || k == i) continue;
                if(e[i][j] && e[j][k] && e[k][i]){
                    ll mn = min({e[i][j], e[j][k], e[k][i]});
                    ans += mn * 2;
                    e[i][j] -= mn;
                    e[j][k] -= mn;
                    e[k][i] -= mn;
                }
            }
        }
    }

    ll tmp = 0;
    for(int i = 1; i <= 4; i++){
        for(int j = 1; j <= 4; j++){
            tmp += e[i][j];
        }
    }
    tmp /= 4;
    tmp *= 3;
    ans += tmp;

    cout << ans << endl;
}
posted @ 2024-10-11 16:41  chenwenmo  阅读(10)  评论(0编辑  收藏  举报