Codeforces Round 421 Div.2 D. Mister B and PR Shifts 思维 乱搞

来源:互联网 发布:cf鬼跳宏数据 编辑:程序博客网 时间:2024/05/29 04:32

题目地址:

http://codeforces.com/contest/820/problem/D

题意:

给一个由[1,n]n(2<=n<=106)个数组成的排列p,定义这个排列的差值是i=ni=1|p[i]i|,现在我们可以对这个排列进行平移(把排列的最后一个数放到排列的最前面),问这个排列的最小差值是多少。

思路:

这题看题解还有点迷,其实也不是很难,就是要自己动手码,代码多改改,多思考下应该都能做出来。
自己随便写几个排列模拟下,不难发现,一次平移其实就是p[i]>ii的值自增了1(除了最后面那个数要特殊考虑),那么所有p[i]i>0的数都会使cost[i]=|p[i]i|小1,反之,所有p[i]i<=0的数都会使cost[i]=|p[i]i|加1,那么我们用一个pos记录p[i]i>0的数的个数,neg记录p[i]i<=0的个数。

特殊考虑一下最后一个数,这个数的cost[n]=|p[i]n|=np[i],一定是neg,又因为我们要对它特殊考虑,所以不在posneg的计数中考虑,所以每次平移sum=sumpos+(neg1)。并且p[n]移到了1的位置,所以特判一下p[n]的大小,对posneg进行修改。

接着就是特殊考虑p[n]cost的影响,很简单,sum=sum|p[n]n|+p[n]1就好了。

最后还是posneg值的变化,我们用一个数组ones[]记录p[i]i的值,每次把当前差值为1的数从pos中拿出,放入neg中就好了。特别注意ones[]数组要开两倍大小。

代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <cstdlib>using namespace std;//#define abs(x) ((x) < 0 ? -(x) : (x))#define MS(x, y) memset(x, y, sizeof(x))typedef long long LL;const int MAXN = 1e6 + 5;int n;int p[MAXN], ones[MAXN << 1];int main() {  while (~scanf("%d", &n)) {    LL sum = 0, mn;    int mn_idx = 0, pos = 0, neg = 0;    MS(ones, 0);    for (int i = 1; i <= n; ++i) {      scanf("%d", p + i);      sum += abs(p[i] - i);      if (p[i] > i) ++pos;      else ++neg;      if (p[i] >= i) ++ones[p[i] - i];    }    mn = sum;    for (int i = 1; i <= n; ++i) {      sum = sum - pos + neg - 1;      sum = sum - abs(p[n - i + 1] - n) + p[n - i + 1] - 1;      if (p[n - i + 1] != 1) {        ++pos;        --neg;      }      pos -= ones[i];      neg += ones[i];      ++ones[p[n - i + 1] - 1 + i];      if (sum < mn) {        mn = sum;        mn_idx = i;      }    }    printf("%I64d %d\n", mn, mn_idx);  }}
阅读全文
0 0
原创粉丝点击