题目:
分析:
看到旋转操作,小编一开始想到的是最小表示法,然后开始愉快的码题,起初还算顺利,但很快就遇到了硬茬——绝对值!
我们先设
x
x
x为题目中的
s
s
s序列
∣
x
1
−
1
∣
+
∣
x
2
−
2
∣
+
∣
x
3
−
3
∣
…
…
|x_1-1|+|x_2-2|+|x_3-3|……
∣x1−1∣+∣x2−2∣+∣x3−3∣……
然后经过一次旋转操作后,就变成了
∣
x
1
−
n
∣
+
∣
x
2
−
2
+
1
∣
+
∣
x
3
−
3
+
1
∣
…
…
|x_1-n|+|x_2-2+1|+|x_3-3+1|……
∣x1−n∣+∣x2−2+1∣+∣x3−3+1∣……
这样我们就可以看到,除去第一个,其他的绝对值中的数都加了
1
1
1
可偏偏就是这个绝对值,使得我们需要统计正、负数的个数
显然,这个用最小表示法是无法实现的,所以我就在最小表示法的思想上套用了权值树状数组
用树状数组来统计负数的个数
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<set>
#include<queue>
#include<vector>
#include<map>
#include<list>
#include<ctime>
#include<iomanip>
#include<string>
#include<bitset>
#include<deque>
#include<set>
#define LL long long
#define ch cheap
#define XJQ %%%
#define offset (n<<1)
using namespace std;
inline LL read() {
LL d=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
return d*f;
}
int min(int x,int y) {return x<y?x:y;}
int now,king;
int x[2000005];int tree[4000010];
int main(){
int n=read();
int big=n*4+1;
for(int i=1;i<=n;i++)
x[i]=read();
LL king=0;
for(int i=1;i<=n;i++)
{
king+=abs(x[i]-i);
for(int j=x[i]-i+offset;j<=big;j+=(j&-j))
tree[j]++;
}
LL now=king;
for(int i=2;i<=n;i++)
{
for(int j=x[i-1]-(i-1)+offset;j<=big;j+=(j&-j))
tree[j]--;
int f=0;
for(int j=offset-i+1;j>0;j-=(j&-j))
f+=tree[j];
now-=f;
now+=(n-1-f);
now=now-abs(x[i-1]-1)+abs(x[i-1]-n);
king=min(king,now);
for(int j=x[i-1]-n-(i-1)+offset;j<=big;j+=(j&-j))
tree[j]++;
}
cout<<king;
return 0;
}