CF819B Mister B and PR Shifts(思维)

做这题细节比较多,要想清楚实际的含义

首先我们肯定是想着枚举看看把哪些提到最前面

对于这题我们发现我们要维护的有两种情况,一种是相减小于0,一种是相减大于0

因此我们分两种情况讨论,并且注意状态的变化,具体细节看代码注释

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll> pll;
const int N=2e6+10;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
ll a[N],sign[N];//sign表示移动i次变成第二类点的个数
int main(){
    ios::sync_with_stdio(false);
    int n;
    cin>>n;
    int i;
    ll pcnt=0,pres=0;
    ll ncnt=0,nres=0;
    for(ll i=1;i<=n;i++){
        cin>>a[i];
        if(a[i]<=i){
            ncnt++;
            nres+=(i-a[i]);
        }
        else{
            sign[a[i]-i]++;
            pcnt++;
            pres+=(a[i]-i);
        }
    }
    ll ans=pres+nres;
    ll k=0;
    for(i=1;i<n;i++){
        pres-=pcnt;//第一类答案都减
        pcnt-=sign[i];//失去变成第二类的
        nres+=ncnt;//第二类答案都是加
        ncnt+=sign[i];//多出来的第二类
        ll x=a[n-i+1];//最后一位移到队首
        ncnt--;
        nres-=(n+1-x);//因为先行计算,导致最后一位到了n+1处,我们先减去他的贡献后把他移到第一位
        if(x>1){
            pcnt++;
            pres+=(a[n-i+1]-1);
            sign[x-1+i]++;//如果他又变成了第二类点,那么要加上他之前移动的i步再计算
        }
        else{
            ncnt++;
        }
        if(ans>pres+nres){
            ans=pres+nres;
            k=i;
        }
    }
    cout<<ans<<" "<<k<<endl;
    return 0;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/13576115.html