• Codeforces Round #421 (Div. 1) B. Mister B and PR Shifts(技巧)


    题目链接:Codeforces Round #421 (Div. 1) B. Mister B and PR Shifts

    题意:

    给你n个数,定义f(x)=∑abs(p[i]-i),即第i个数与i的差值和。

    每次可以将最后一个数放在第一个数,现在问你最小的f(x)是多少,和操作多少次。

    题解:

    这是一个比较有技巧性的题目。

    就是考如何来维护每个数的偏移量。

    开一个数组cnt,cnt[i]表示原始数列的一个数在实际位置的左边并且距离实际位置为i的个数。

    l表示一个数在实际位置的左边的个数,r表示一个数在实际位置右边的个数。

    每次移动的时候l-=cnt[i-1],r+=cnt[i-1],即第i次移动的时候要将对应偏移量的数更新。

    相当于偏移量为i-1的数从左边跑到了右边。

    显然不考虑最后一个数的时候每次移动,整体的答案变化为sum=sum-l+(r-1)。(减1是因为不考虑最后一个数)

    然后我们再特判最后一个数。从最后一个移到第一个,对答案的贡献为abs(a[n]-n)+a[n]-1。

    这里的n随着i的变化而变化。

    移动完后,我们还得维护一下l,r,cnt。

    由于将最后一个数移到了第一个位置。显然这个数是在实际位置的左边(为了方便,等于实际位置也算在左边),所以l++,r--。

    由于cnt[i]记录的是原始数列的偏移量,第i次将最后一个数移到了第一个位置,cnt[a[n]-1+i]++。

    表示从第i次开始,偏移量为a[n]-1的数多了一个。

    细节上还需要自己理解理解。

     1 #include<bits/stdc++.h>
     2 #define F(i,a,b) for(int i=(a);i<=(b);++i)
     3 using namespace std;
     4 typedef long long ll;
     5 
     6 const int N=2e6+7;
     7 int cnt[N],a[N],idx,l,r,n;
     8 ll ans,sum;
     9 
    10 int main(){
    11     scanf("%d",&n);
    12     F(i,1,n)
    13     {
    14         scanf("%d",a+i);
    15         if(a[i]>=i)l++,cnt[a[i]-i]++;else r++;
    16         ans+=abs(a[i]-i);
    17     }
    18     sum=ans,idx=0;
    19     F(i,0,(n-1))
    20     {
    21         l-=cnt[i],r+=cnt[i];
    22         sum=sum-l+r-abs(a[n-i]-n)+a[n-i]-2;
    23         cnt[a[n-i]+i]++,l++,r--;
    24         if(sum<ans)ans=sum,idx=i+1;
    25     }
    26     printf("%lld %d
    ",ans,idx);
    27     return 0;
    28 }
    View Code
  • 相关阅读:
    PTA 7-9 空心字母金字塔 (10分)【待优化】
    PTA 7-12 交换最小值和最大值 (15分)
    PTA 7-8 分队列 (10分)
    PTA 7-6 计算阶乘和 (10分)
    PTA 7-5 得分计算器 (20分)
    PTA 7-4 有重复的数据 (10分)
    PTA 7-1 数组元素循环右移问题 (20分)
    PTA 7-1 换硬币 (20分)
    PTA 7-6 又来一个上三角数字三角形 (10分)【待完善】
    PTA 7-5 画菱形 (10分)
  • 原文地址:https://www.cnblogs.com/bin-gege/p/7125975.html
Copyright © 2020-2023  润新知