注:本题运用线段树只是用于第一次求逆序数,降低时间复杂度。至于后面的筛选,用的是逆序数的一个性质,就是两相邻数字交换位置,逆序数+1或者-1.
/*
HDOJ1754
作者:陈佳润
时间:2013-03-29
*/
#include<iostream>
using namespace std;
#define MaxSize 5001
int Tree[MaxSize*4];
//计算父节点
void PushUp(int p)
{
Tree[p]=Tree[p<<1]+Tree[p<<1|1];
}
//创建线段树
void Create(int p,int l,int r){//可存在[a,a]的节点
Tree[p]=0;//初始化,如果有用下面的语句读入,则无需
if(l==r){//当达到叶子节点时
//scanf("%d",&Tree[p]);//当需要读的时候
return;
}
int mid=(l+r)>>1;//计算中间值
Create(p<<1,l,mid);//向左递归
Create(p<<1|1,mid+1,r);//向右递归
//PushUp(p);//计算节点值
}
//修改线段树
void Add(int i,int add,int p,int l,int r){//增加叶子节点数值
if(l==r){//当达到叶子节点时,加上修改值
Tree[p]+=add;
return ;
}
int mid=(l+r)>>1;//计算中间值
if(i<=mid)//向左递归
Add(i,add,p<<1,l,mid);
else//向右递归
Add(i,add,p<<1|1,mid+1,r);
PushUp(p);//计算节点值
}
//查询线段树
int Query(int p,int l,int r,int L,int R){
if(R>=r&&L<=l)//当查询的区间比树的区间大时
return Tree[p];
int sum=0;//当前节点的值
int mid=(l+r)>>1;//计算中间值
if(L<=mid)//向左递归
sum+=Query(p<<1,l,mid,L,R);
if(R>mid)//向右递归
sum+=Query(p<<1|1,mid+1,r,L,R);
return sum;
}
int main()
{
int n,i,a[MaxSize],sum,ret;
while(scanf("%d",&n)!=EOF){
Create(1,0,n-1);
sum=0;
for(i=0;i<n;i++){
scanf("%d",&a[i]);
sum+=Query(1,0,n-1,a[i],n-1);
Add(a[i],1,1,0,n-1);
}
ret=sum;
for(i=0;i<n;i++){
sum+=(n-1)-a[i]-a[i];
if(ret>sum)
ret=sum;
}
cout<<ret<<endl;
}
}