CF820D

思维题

题意:

给出一个\(n\)元素排列\(p[]\),定义数组\(p[]\)的误差值为\(\sum\limits_{i=1}^{i=n} |p[i]-i|\).每次操作都把下标为\(n\)的数放到下标为\(1\)的位置,其他数依次右移,问在通过几次操作后能使得误差值最小

较麻烦做法:差分

正解是差分,设\(d[i]\)为i次操作后的误差值,考虑\(p[j]\)\(d[i]\)的贡献,发现i是一段连续的区间,即区间\([l,r]\)同时加上某个等差数列。

区间\([l,r]\)加上\(k*(x-l)+b\)\((l≤x≤r)\) 设置两个数列\(d[i],f[i]\)

  1. \(d[l]+=b,d[r+1]-=b\).剩下\(k*(x-l)\).
  2. \(f[l+1]+=k. f[r+1]-=k\)\(f\)求一次前缀和,
  3. \(d[r+1]\)减去\((r-l+1)\)\(k\)表示该等差数列的结束.
  4. 然后对\(d[i]\)加上\(f[i]\)的前缀和,\(d[i]\)在加上本身的前缀和即可得到最后真正的\(d[i]\).

\(f[i]\)的前缀和就是\(i\)位置最终的\(k\)值,再求一次前缀就是\(1\)\(i\)\(k\)之和,但由于我们只需要\([l+1,x]\)\(k\)值之和,所以还要将多出的减掉

不过我们还有更简单的解法

法二:直接模拟:

注意到对于每个p[i],使得\(p[i]==i\)的时间点只有一个,计算出这个时间点,若p[i]!=1,让该时间内由p[i]>i变成p[i]==i的数++

考虑每次右移操作造成的影响,可以看做\(1\)\(n-1\)位置的数\(i+1\)\(n\)位置\(i=1\),若能维护出实时的大于\(0\)的个数和小于\(0\)的个数,再单独考虑最后一个数,就可以动态的计算答案。由于使得\(p[i]==i\)的时间点只有一个,故容易预处理每个时间的变化量。

//细节太繁琐,我崩溃了
//果然还是太菜了 
#include
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))
#define fo(i,a) for(int i=0;ii)cp++;
        if(p[i]<=i)cn++;
        sum+=abs(p[i]-i);
    }
    mink=0;
    ans=sum;
    go(k,1,n-1){
        sum=sum+cn-1-cp;
        sum+=2*p[(n-k)%n+1]-1-n;
        cp++,cn--;
        cp-=idx[k],cn+=idx[k];
        if(sum

你可能感兴趣的:(CF820D)