分析:主要是利用線段樹求逆序數,建的是一棵空樹,然後每插入一個點之前,統計大於這個數的有多少個,直到所有的數都插入完成,就結束了逆序樹的統計。
要得出答案主要是利用了一個結論,如果是0到n的排列,那麼如果把第一個數放到最後,對於這個數列,逆序數是減少y[i],而增加n-1-y[i]的。(可以這樣想,因爲是第一個數,所有的數都在它後面,那麼在當前位置pos比它大的數也在它後面,那麼第一個數調到後面之後,在pos不成立的逆序數就成立了,所以多了n-y[i]-1,但是也少了在pos成立的逆序數,即y[i]個)
// Time 46ms; Memory 340K
#include<iostream>
#include<cstdio>
#define maxn 1<<14
using namespace std;
int size,n,y[5010];
struct line
{
int l,r;
int n;
}a[maxn];
void init()
{
int i;
for(n=1;n<size;n<<=1);
for(i=n;i<2*n;i++)
{
a[i].l=a[i].r=i-n+1;
a[i].n=0;
}
for(i=n-1;i>0;i--)
{
a[i].l=a[2*i].l;
a[i].r=a[2*i+1].r;
a[i].n=0;
}
}
void insert(int i,int x)
{
if(x>=a[i].l && x<=a[i].r) a[i].n++;
if(a[i].l==a[i].r) return;
int mid=(a[i].l+a[i].r)/2;
if(x>mid) insert(2*i+1,x);
else insert(2*i,x);
}
int find(int x,int y,int i)
{
if(x<=a[i].l && y>=a[i].r)
{
return a[i].n;
}
int mid=(a[i].l+a[i].r)/2;
int sum1=0,sum2=0;
if(y>mid) sum1=find(x,y,2*i+1);
if(x<=mid) sum2=find(x,y,2*i);
return sum1+sum2;
}
int main()
{
int j,sum,min;
while(scanf("%d",&size)!=EOF)
{
init();
sum=0;
for(j=0;j<size;j++)
{
scanf("%d",&y[j]);
y[j]++;
insert(1,y[j]);
sum+=find(y[j]+1,size,1);
}
min=sum;
for(j=0;j<size-1;j++)
{
sum-=y[j]-1;
sum+=size-y[j];
if(min>sum) min=sum;
}
printf("%d\n",min);
}
return 0;
}