题意:给你N个数,要求统计它的所有形式的逆序对的最小值。它的所有形式的意思是,不断将数组开头的第一个数放到数组的最后面。
分析:主要是利用线段树求逆序数,建的是一棵空树,然后每插入一个点之前,统计大于这个数的有多少个,直到所有的数都插入完成,就结束了逆序树的统计。
要得出答案主要是利用了一个结论,如果是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;
}