用线段树求原始序列的逆序数,然后再递推求其它时候的逆序数,比较得最小值。
线段树求逆序数的原理跟树状数组一样,都是利用区间求和。我这里为了方便自己写的线段树,把下标调整成了从1开始到n
把当前第一个元素移动到末尾位置时,总逆序数的变化是:加上大于该数的个数,减去小于该数的个数。
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <algorithm>
#define MAXN 5005
#define INF 2139062143
#define ll long long
using namespace std;
const int MAX_N = 1<< 13;
int sum[MAX_N];
int mxn,n;
void Init()
{
mxn=1;
while(mxn<n) mxn*=2;
memset(sum,0,sizeof(sum));
}
void update(int k,int val)
{
k+=mxn-1;
sum[k]=val;
while(k/2>0)
{
k=k/2;
sum[k]+=val;
}
}
int query(int a,int b,int k,int l,int r)
{
if(r<a||b<l) return 0;
if(a<=l&&r<=b) return sum[k];
else
{
int v1=query(a,b,2*k,l,(l+r)/2);
int v2=query(a,b,2*k+1,(l+r)/2+1,r);
return v1+v2;
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int arr[MAXN]= {0};
Init();
int sum=0;
for(int i=0; i<n; ++i)
{
scanf("%d",&arr[i]);
arr[i]++;
sum+=query(arr[i],n,1,1,mxn);
update(arr[i],1);
}
int ans=sum;
for(int i=0; i<n; ++i)
{
sum=sum+(n-arr[i])-(arr[i]-1);
ans=min(sum,ans);
}
printf("%d\n",ans);
}
return 0;
}