[CF819B] Mister B and PR Shifts

题目

原题链接

解说

第一思路暴力,时间效率\(O(n^2)\),肯定\(T\)这次试都没试,想办法优化吧。
\(O(n^2)\)的效率是因为我们有两层循环:枚举哪一位移动到队首了,以及计算现在的偏移值。枚举估计是省不了,那么我们就要想办法把计算偏移值优化到常数级,这样时间效率为\(O(n)\),应该问题不大了。
我们可以注意到,每次移动时,如果原来\(a[i]<=i\),由于\(a[i]\)对应的\(i\),变大了,所以\(a[i]-i\)负的更多了,绝对值变大了,且每移动一位每个数的绝对值增大\(1\);相对的,对于\(a[i]>i\),由于\(i\)增大了,绝对值每个数减小\(1\),因此每次操作我们只需让\(sumpositive\)减去\(cntpositive\),让\(sumnegative\)加上\(cntnegative\),之后要更新\(cntpositive\)\(cntnegative\)的值。位于队末的元素比较特殊,它的\(i\)\(n\)变成了\(1\),需要特殊处理一下。这样的话我们就把时间效率降到\(O(n)\)了。
有一个小方法需要说一下,就是我们怎么判断一个差为正的数何时转为差为负的数(指小于等于零)。枚举是不可能的,这样时间效率就又回\(n^2\)了。想一下,如果一个数正出来\(x\),那么就一定会在\(x\)轮之后转负(当然是指没有被扔到队首的情况,这时候我们特殊处理即可),因此我们在开始输入时就统计出来一个\(count\)数组统计每个正出来的数的\(x\)值即可。
一些我踩的坑和部分注解在代码里给出。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2000000+3;
typedef long long ll;
ll n,a[1000000+3],sump,sumn,pos,ans,cntp,cntn,Count[maxn];
//注意maxn不是n的范围,Count的下标是差的值,因此应开到差的最大值
int main(){
	scanf("%lld",&n);
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		if(a[i]<=i){
			cntn++;
			sumn+=i-a[i];
		}
		else{
			cntp++;
			sump+=a[i]-i;
			Count[a[i]-i]++;
		}
	}
	ans=sump+sumn;
	pos=0;//注意这里的初始化,一定记得把pos初始化为0,代表不用操作的情况
	for(ll i=1;i<=n-1;i++){
		sump-=cntp;
        cntp-=Count[i];
        sumn+=cntn;
        cntn+=Count[i];
        sumn-=n+1-a[n-i+1];
		cntn--;
        if(a[n-i+1]>1){
			Count[a[n-i+1]-1+i]++;
			sump+=a[n-i+1]-1;
			cntp++;
		}
        else cntn++;
		if(sumn+sump<ans){
			ans=sumn+sump;
			pos=i;
		}
	}
	printf("%lld %lld",ans,pos);
	return 0;
}

幸甚至哉,歌以咏志。

posted @ 2020-05-08 22:02  DarthVictor  阅读(172)  评论(0编辑  收藏  举报
莫挨老子!