hdu 1394 Minimum Inversion Number(单点更新)

题意:给你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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章