CF819B Mister B and PR Shifts 思维题

分析

这道题\(n\leq10^{6}\),显然\(n^{2}\)的暴力是无法解决问题的

那么我们可以考虑数列的某一种性质

因为最终的答案是\(\sum{n \atop i=1} |p_i - i|\),和绝对值有关,所以我们把数列中的数分为两类

第一类是\(p_i-i>0\),第二类是\(p_i-i\leq0\)

我们可以开四个变量:\(Zcnt\) 记录第一类数的个数,\(Ztot\) 记录第一类数对结果的贡献,\(Fcnt\) 记录第二类数的个数,\(Ftot\) 记录第二类数对结果的贡献

这些变量的初始值我们可以预处理出来

每次我们把数列中位置最靠后的元素拿出来,放在最前面,其他的元素向后移动一位

我们先不考虑临界元素和特殊情况

一个很显然的结论是,除了扔到队首的那一个元素,第一类数每次做出的贡献之和都要减去\(Zcnt\),第二类数每次做出的贡献之和都要加上\(Fcnt\)

但是\(Zcnt\)\(Fcnt\)的个数并不是恒定不变的,有两种特殊情况

1、一个数由第一类数变为第二类数,这时的临界条件是\(p_i-i=0\)

那么我们只需要在读入的时候预处理每一个数发生临界变化的时间就可以了

2、一个数由第二类数变为第一类数,这时只有可能是原来在队尾的数扔到了队首

这时我们只需要特判一下就可以了

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=1e7+5;
ll n,Zcnt,Fcnt,Ztot,Ftot;
ll a[maxn],judone[maxn];
int main(){
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    for(ll i=1;i<=n;i++){
        if(a[i]<=i){
            Fcnt++;
            Ftot+=(i-a[i]);
        } else {
            judone[a[i]-i]++;
            Zcnt++;
            Ztot+=(a[i]-i);
        }
    }
    ll ans=Ztot+Ftot,jl=0;
    for(ll i=1;i<n;i++){
        Ztot-=Zcnt;
        Zcnt-=judone[i];
        Ftot+=Fcnt;
        Fcnt+=judone[i];
        ll x=a[n-i+1];
        Ftot-=n+1-x,--Fcnt;
        if(x>1)++judone[x-1+i],Ztot+=x-1,++Zcnt;
        else ++Fcnt;
        if(ans>Ftot+Ztot) ans=Ftot+Ztot,jl=i;
    }
    printf("%lld %lld\n",ans,jl);
    return 0;
}
posted @ 2020-05-09 12:21  liuchanglc  阅读(129)  评论(0编辑  收藏  举报