Codeforces Round #421 (Div. 2) D. Mister B and PR Shifts

Codeforces Round #421 (Div. 2) D. Mister B and PR Shifts


题意:给一个长度为(n)的排列,每次可以向右循环移位一次,计算(sum_{i=1}^{n}|p_i - i|)的最小值,并求最小值是在第几次移动时取到的。

思路:我们注意到对于每个(p_i),其位置都取遍了(1-n),那么可以分成(p_i > i) 和 $p_i <= i $两种

对于前者 每次移动贡献-1,后者贡献+1,而且我们可以容易计算出每次移动后这两者的数量,于是可以模拟(O(n))

#include<bits/stdc++.h>
#define P pair<int,int>
#define LL long long
using namespace std;
const int N = 1e6 + 10;
int n, p[N],cnt[N];

int main(){
    cin>>n;
    for(int i = 1;i <= n;i++) cin>>p[i];
    int L = 0,R = 0;
    LL res = 0,ans;
    for(int i = 1;i <= n;i++){
        res += abs(p[i] - i);
        cnt[(p[i]-i+n)%n]++; ///移动x次变成p[i] <= i的个数
        if(p[i] <= i) R++;
        else L++;
    }
    ans = res;
    int idx = 0;
    for(int k = 1;k < n;k++){
        res -= L; /// 左边的减少一
        res += R - 1; ///右边的增加一,右端点除外
        res += p[(n-k)%n+1] - n + p[(n-k)%n+1] - 1;///处理右端点
        L -= cnt[k] - 1;
        R += cnt[k] - 1;
        if(res < ans){
            idx = k,ans = res;
        }
    }
    cout<<ans<<" "<<idx<<endl;
   return 0;
}
原文地址:https://www.cnblogs.com/jiachinzhao/p/7123766.html