CF819B 偏移量

1 CF819B 偏移量

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

Some time ago Mister \(B\) detected a strange signal from the space, which he started to study.

After some transformation the signal turned out to be a permutation \(p\) of length \(n\) or its cyclic shift. For the further investigation Mister \(B\) need some basis, that's why he decided to choose cyclic shift of this permutation which has the minimum possible deviation.

Let's define the deviation of a permutation \(p\) as \(\sum\limits_{i=1}^{i=n}|p[i]-i|\).

Find a cyclic shift of permutation \(p\) with minimum possible deviation. If there are multiple solutions, print any of them.

Let's denote id \(k (0 ≤ k < n)\) of a cyclic shift of permutation \(p\) as the number of right shifts needed to reach this shift, for example:

  • \(k = 0:\) shift \(\ p_1, p_2, ... p_n,\)
  • \(k = 1:\) shift \(\ p_n, p_1, ... p_{n-1},\)
  • ...,
  • \(k = n - 1\): shift \(\ p_2, p_3, ... p_n, p_1.\)

数据范围:\(2 ≤ n ≤ 10^6\)

3 题解

我们发现:所有的 \(p_i\) 都可以分为两类,一类是大于 \(i\) 的,一类是小于等于 \(i\) 的。对于大于 \(i\) 的那一类,答案就是 \(p_i - i\),而对于小于等于 \(i\)\(p_i\),答案就是 \(i - p_i\)。容易发现:对于每一次变换,我们从第 \(1\) 个数一直到第 \(n-1\) 个数所对应的新的 \(i\) 一定是之前的 \(i\) 加上 \(1\)。此时,我们容易发现:所有的第一类的 \(p_i\) 之和会减少第一类的 \(p_i\) 个数这么多的数,而第二类的 \(p_i\) 之和会增加第二类的 \(p_i\) 个数这么多的数。由于此时最后一个数已经不算是第二类数了,所以我们要提前将第二类 \(p_i\) 的个数减去 \(1\)。此时,第二类的数的总和还要减去 \(n - p_n\),为了减去当前这个数离开前所提供的价值。此时由于这个数最小也只是 \(1\),所以在调到第一个数后,一定不会变成 \(p_i < i\),此时也就对于第二类数的总和没有价值了。此时还有一个问题,在移动过程中,某些数可能从第一类数变为了第二类数,也就是 \(i\) 慢慢增加最终超过 \(p_i\)。所以我们用一个 \(tmp\) 数组记录在某一轮变换后,会有多少个第一种情况的数变成第二种情况,将第一种情况的总个数减去这些数,将第二种情况的总个数加上这些数即可。注意如果最后一个数在调整到第一个数后变成了第一种情况的数,那么我们也需要将它变成第二种情况的轮数计算出来,并且将第一种情况的 \(p_i\) 个数和总和更新一下。

4 代码(空格警告):

#include <iostream>
using namespace std;
const int N = 1e7+10;
#define int long long
int n, zcnt, zsum, fcnt, fsum, ans, k, x;
int p[N], tmp[N];
signed main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> p[i];
    for (int i = 1; i <= n; i++)
    {
        if (p[i] <= i)
        {
            fcnt++;
            fsum += (i - p[i]);
        }
        else
        {
            zcnt++;
            tmp[p[i] - i]++;
            zsum += (p[i] - i);
        }
    }
    ans = zsum + fsum;
    for (int i = 1; i < n; i++)
    {
        fcnt--;
        zsum -= zcnt;
        fsum += fcnt;
        zcnt -= tmp[i];
        fcnt += tmp[i];
        x = p[n-i+1];
        fsum -= n - x;
        if (x > 1)
        {
            tmp[x-1+i]++;
            zsum += x - 1;
            zcnt++;
        }
        else fcnt++;
        if (ans > zsum + fsum) ans = zsum + fsum, k = i;
    }
    cout << ans << " " << k;
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-04-08 21:34  David24  阅读(56)  评论(0编辑  收藏  举报