题目链接:http://codeforces.com/problemset/problem/820/D
题目大意:
给出一个\(n\)元素数组\(p[]\),定义数组\(p[]\)的误差值为\(\sum\limits_{i=1}^{i=n} |p[i]-i|\).每次操作都把下标为\(n\)的数放到下标为\(1\)的位置,其他数依次右移,问在通过几次操作后能使得误差值最小
知识点: (void)
解题思路:
见注释。
AC代码:
1 #include2 3 using namespace std; 4 typedef long long ll; 5 const int maxn=1e6+5; 6 ll p[maxn],has[maxn<<1]; 7 8 int main(){ 9 ll n; 10 ll bigger=0,smaller=0,equ=0,ans1=0,ans2=0; //bigger 记录目前比其下标大的数的个数,small 记录比其下标小的,equ 记录等于其下标的,最终输出是 ans1 ans2 11 scanf("%I64d",&n); 12 for(ll i=1LL;i<=n;i++){ 13 scanf("%I64d",&p[i]); 14 if(p[i]>i){ 15 bigger++; 16 has[p[i]-i]++; //has[X] 记录比下标大 X 的数的个数 17 } 18 else if(p[i]==i){ 19 equ++; 20 has[0]++; 21 } 22 else smaller++; 23 ans1+=abs(p[i]-i); 24 } 25 ll temp=ans1; //临时记录ans1 26 27 //首先,请注意:我后面提到的数字其实都是数字与下标的差值,因为我们着重研究的是这个 28 for(ll last=n-1LL,now=1LL;last>=1LL;last--,now++){ //last 记录目前下标为n的数的位置;now 记录目前是第几号变换,在此处理解为一条“零线”,除了下标为n的数之外的所有数减去 now 即为现在的数 29 temp+=(equ+smaller); 30 temp-=bigger; //从 now-1 变换到 now 后,所有数的下标加一(不考虑下标为n的数),则原来小于或等于下标的数对于ans1的贡献值增大1,大于下标的数对于ans1的贡献值减小1 31 32 smaller+=equ; //原本等于下标的数都变成小于了 33 bigger-=has[now]; //原本等于 now+1(即现在的now, 其实就是原本等于1)的数现在都变成了0 34 //接下来处理之前下标是n,现在下标是1的数 35 if(p[last+1]>=last+1LL) 36 has[p[last+1]-last-1]--; //先抹除其原来在has[]中的记录 37 has[p[last+1]-1+now]++; //重新记录,注意:此时的“零线”已经抬高了,相应的也要加上 now 38 equ=has[now]; //新的equ其实就是那些现在在“零线”上的数 39 if(p[last+1]>1LL) bigger++; 40 smaller=n-equ-bigger; //显然,smaller + equ + bigger = n 41 42 temp-=abs(p[last+1]-n-1LL); //特殊处理原本下标为n的数 43 temp+=abs(p[last+1]-1LL); 44 if(temp<ans1){ 45 ans1=temp; 46 ans2=now; 47 } 48 } 49 printf("%I64d %I64d\n",ans1,ans2); 50 51 return 0; 52 }