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;
}

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