Description
这是一道原题。
给定一长度为 n 的序列 s,定义其健美值为:
∑|si−i|
(i∈1~n)
因为猫喜欢健美,所以猫希望减小 s 的健美值,以衬托猫的健美。为了达到猫的目的,猫希望你对序列进行旋转操作,一次旋转操作可以使序列中的所有元素前移一位,并使 s1移动到 sn。
你可以进行任意次旋转操作,猫希望旋转后的健美值最小,请找出这个最小值。
Input
n
s1 s2 · · · sn
Output
一个数,代表最小的健美值。
Sample Input 1
3
2 3 1
Sample Output 1
0
Sample Input 2
6
4 2 2 4 2 5
Sample Output 2
6
Data Constraint
对于 30% 的数据 n≤10^3。
对于 70% 的数据 n≤10^5。
对于 100% 的数据 n≤2*10^6 , ∀i 1≤si≤n。
解题思路
当一个数si减去i时,我用数组z模拟了一个数轴,记录每个点上的数有多少个,再记录大于零的数和小于零的数分别有多少个。对于每次向后枚举第一个i的位置,朴素的做法是把每个数都加一;乱搞做法就是把数轴原点减一,再通过大于零和小于零的个数更新当前的答案。
代码
#include<bits/stdc++.h>
using namespace std;
int n,O,fu,zheng;
long long now,ans;
int a[2000005];
int z[6000005];
int main(){
scanf("%d",&n);O=2*n;
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=n;++i){
++z[O+a[i]-i];
if(a[i]-i<0) ++fu;
if(a[i]-i>0) ++zheng;
now+=abs(a[i]-i);
}
ans=now;
for(int i=1;i<=n-1;++i){
--z[O+a[i]-1];
if(a[i]-1>0) --zheng;
now-=abs(a[i]-1);
now=now-fu+zheng+z[O];
zheng+=z[O];fu-=z[O-1];
--O;
++z[O+a[i]-n];
if(a[i]-n<0) ++fu;
now+=abs(a[i]-n);
ans=min(ans,now);
}
printf("%lld",ans);
return 0;
}