程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> hdu 1394 Minimum Inversion Number(線段樹)

hdu 1394 Minimum Inversion Number(線段樹)

編輯:C++入門知識

hdu 1394 Minimum Inversion Number(線段樹)


Minimum Inversion Number

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 13797 Accepted Submission(s): 8423



Problem Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.

Input The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.

Output For each case, output the minimum inversion number on a single line.

Sample Input
10
1 3 6 9 0 8 5 7 4 2

Sample Output
16

Author CHEN, Gaoli
Source ZOJ Monthly, January 2003

題意:輸入n,下面給出n個數,分別是0-n,順序不定,可以通過對序列進行移動,每次移動把最前面的數移到後面,問通過移動形成的序列,使得得到的逆序數最少
這題數據較小,可以普通暴力水過,當然比較正規的方法是線段樹。
題目要求求最小的逆序數。觀察數列,可以發現一個規律:將數列第一個數移動到最後一位之後,逆序數變為sum+n-1-2*a[i]。這個規律還是比較容易推的。
所以我們要做的就是求第一個數列的逆序數,其他的可以通過這個規律求得,最後找一個最小值即可。
我從別人那裡看來的解釋=_=:
先以區間[0,9]為根節點建立val都為0的線段樹,
再看看怎樣求下面序列的逆序數:
1 3 6 9 0 8 5 7 4 2
在線段樹中插入1, 插入之前先詢問區間[1,9]已插入的節點數(如果存在,必與1構成逆序) v1=0
在線段樹中插入3, 插入之前先詢問區間[3,9]已插入的節點數(如果存在,必與3構成逆序) v2=0
在線段樹中插入6, 插入之前先詢問區間[6,9]已插入的節點數(如果存在,必與6構成逆序) v3=0
在線段樹中插入9, 插入之前先詢問區間[9,9]已插入的節點數(如果存在,必與9構成逆序) v4=0
在線段樹中插入0, 插入之前先詢問區間[0,9]已插入的節點數(如果存在,必與0構成逆序) v5=4
在線段樹中插入8, 插入之前先詢問區間[8,9]已插入的節點數(如果存在,必與8構成逆序) v6=1
在線段樹中插入5, 插入之前先詢問區間[5,9]已插入的節點數(如果存在,必與5構成逆序) v7=3
在線段樹中插入7, 插入之前先詢問區間[7,9]已插入的節點數(如果存在,必與7構成逆序) v8=2
在線段樹中插入4, 插入之前先詢問區間[4,9]已插入的節點數(如果存在,必與4構成逆序) v9=5
在線段樹中插入2, 插入之前先詢問區間[2,9]已插入的節點數(如果存在,必與2構成逆序) v10=7
累加v1+……+v10 =22,這就是1 3 6 9 0 8 5 7 4 2的逆序數了.
就是一個個往線段樹中插入數,每次插入前詢問比這個數大的數有幾個。其實就是問一個區間裡插入了多少數字!
#include
#include
#define M 5005
struct tree{
	int l,r,sum;
}tree[M<<2];
int x[M];
int max(int a,int b){
	if(a>b)return a;
	else return b;
}
void build(int l,int r,int root)
{
	tree[root].l=l;
	tree[root].r=r;
	tree[root].sum=0;
	if(l==r)return;
	int mid=l+r>>1;
	build(l,mid,root<<1);
	build(mid+1,r,root<<1|1);
}
void pushup(int root){
	if(tree[root].l==tree[root].r)return;
	tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}
void update(int l,int r,int root)
{
	if(tree[root].l==l&&tree[root].r==r){
		tree[root].sum++;
		return;
	}
	int mid=tree[root].l+tree[root].r>>1;
	if(r<=mid)update(l,r,root<<1);
	else if(l>mid)update(l,r,root<<1|1);
	else {
		update(l,mid,root<<1);
		update(mid+1,r,root<<1|1);
	}
	pushup(root);
}
int  query(int l,int r,int root)
{
	if(tree[root].l==l&&tree[root].r==r){
		return tree[root].sum;
	}
	int mid=tree[root].l+tree[root].r>>1;
	if(r<=mid)return query(l,r,root<<1);
	else if(l>mid)return query(l,r,root<<1|1);
	else return query(l,mid,root<<1)+query(mid+1,r,root<<1|1);
}
int main()
{
	int n,m,i,j,k,a[M],b,tot;
	while(scanf(%d,&n)!=EOF)
	{
		tot=0;
		build(0,n-1,1);
		for(i=0;i

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved