做这题细节比较多,要想清楚实际的含义
首先我们肯定是想着枚举看看把哪些提到最前面
对于这题我们发现我们要维护的有两种情况,一种是相减小于0,一种是相减大于0
因此我们分两种情况讨论,并且注意状态的变化,具体细节看代码注释
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }