Codeforces Round #421 (Div. 2) D. Mister B and PR Shifts

这学期暑假集训的第一次博客。 ps:貌似某人好久没有写博客了吧
Codeforces Round #421 (Div. 2)/D
题意:
有n个数,从1到n乱序排列,定义这n个数的秩序值为∑(a[i]-i) (1<=i<=n), 你每次将这个数组向右循环移位p次,
问p等于多少时,这n个数的秩序值最小?

暴力的方法很好想,n*n遍历一下,因为n = 1e6,这时候就要换成 nlog[n]或者n的复杂度才行。其实这道题目就可以从复杂度出发,仔细思考有没有哪里可以降复杂度的。
最后我是参考了一位大神的做法,cf题解全英文还很长看的没有耐心。。
贴上他的做法(@Jaihk662):

记录下每个数是在目标位置的左边还是右边,存下所有在目标左边的数
cur[i]表示初始数组中有多少个数在它目标左边第i个位置上
->注意要存下一开始在目标左边(包括在目标上)的个数L,以及目标右边的数的个数r
之后直接模拟右移,对于第i次右移,L = L-cur[i-1], r = r+cur[i-1],即当次位移刚好有cur[i-1]个数原本在目标位置左边及目标上跑到了目标位置的右边,
每次的秩序值便是初始秩序值-L+r+特判最后一个数移到第一个数所产生的影响
但注意计算之后L还要+1,r还要-1因为最后一个数跑到了第一个

#include <iostream>  
#include <cstdio>  
#include <cstdlib>  
#include <cstring>  
#include <cmath>  
#include <stack>  
#include <bitset>  
#include <queue>  
#include <set>  
#include <map>  
#include <string>  
#include <algorithm>  
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e6+5;
const LL inf = 0x3f3f3f3f3f3f3f3f;
LL mod = 1e9+7;
double eps = 0.00000001;
double PI = acos(-1);

int p[maxn],pl[2*maxn];   //p是初始串,pl是 p[i]比i大了多少的个数

int main() {
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++) scanf("%d",&p[i]);
    LL sum = 0,ans;
    int pos = 0;
    clr(pl,0);
    int l = 0,r = 0;
    for(int i = 1;i <= n;i++) {
        sum += abs(p[i]-i);
        if(p[i] >= i) {
            l++;
            pl[p[i]-i]++;  //l存p[i]-i中大于i的个数,这种情况下大于的数字的个数
        }
        else r++;
    }
    ans = sum;
    for(int i = 1;i < n;i++) {
        l -= pl[i-1];
        r += pl[i-1];
        sum = sum - l + r - abs(n+1 - p[n-i+1]) + p[n-i+1]-1;
        pl[p[n-i+1]-1+i]++;
        if(sum < ans) {
            ans = sum;
            pos = i;
        }
        l++;
        r--;
    }
    cout<<ans<<' '<<pos<<endl;
}
//每一次更新sum的值的时候只需要O(1),因为记录了差值,这样复杂度上就可以啦。

加油,机会总是给有准备的人留着。
fight!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值