您的位置:首页 > 其它

hdu 1394 Minimum Inversion Number(线段树-单点更新)

2015-08-03 13:59 453 查看
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


就是给出一串数,当依次在将第一个数变为最后一个数的过程中,要你求它的最小逆序数。

先建的是一棵空树,然后每插入一个点之前,统计大于这个数的有多少个,直到所有的数都插入完成,就结果了初始逆序树的统计。

#include <iostream>

#include <stdio.h>

using namespace
std;

#define maxn 5005

int sum[3*maxn];

void PushUp(int root)

{

sum[root]=sum[root<<1]+sum[(root<<1)+1];

}

void Build(int L,int R,int root)

{

sum[root]=0;

if(L==R)

return;

int m=(L+R)>>1;

Build(L,m,root<<1);

Build(m+1,R,(root<<1)+1);

}

void Update(int L,int R,int root,int pos)

{

if(L==R)

{

sum[root]++;

return;

}

int m=(L+R)>>1;

if(pos<=m)

Update(L,m,root<<1,pos);

else

Update(m+1,R,(root<<1)+1,pos);

PushUp(root);

}

int Query(int ql,int qr,int L,int R,int root)

{

if(ql<=L && qr>=R)

{

return sum[root];

}

int m=(L+R)>>1;

int ans=0;

if(ql<=m)

ans+=Query(ql,qr,L,m,root<<1);

if(qr>m)

ans+=Query(ql,qr,m+1,R,(root<<1)+1);

return ans;

}

int val[maxn];

int main()

{

int n;

while(scanf("%d",&n)!=EOF)

{

Build(0, n-1,1);
//建空树(初始化)

int sum2=0;

for (int i=0; i<n; i++)

{

scanf("%d",&val[i]);

sum2+=Query(val[i], n-1,0, n-1,
1);

/*

query求出区间[val[i], n - 1]中的点数,因为这个区

间内的点都比val[i]先插入且比seq[i]大(而坐标却比val[i]的坐标小,

故他们之间是逆序数),所以,这个区间内的点的个数就等于val[i]的逆序数,

把这些点的逆序数全加起来,就得到整个序列的逆序数sum。

*/

Update(0, n-1,1,val[i]);
//插入val[i],更新线段内的点数。

}

int ans=sum2;

for (int i=0; i<n; i++)

{

/*

因为序列为[0, n-1],若最前面一个数为x(其坐标最小),序列中比x

小的数为[0, x-1],
共x个,比x大的数为[x+1, n-1],

共n-x-1个,将x移到最后(其坐标最大),比x小的数的逆序数均减1,

x的前面比x大的数有n-x-1个,x的逆序数增加n-x-1。

所以新序列的逆序数为原序列的逆序数加上n-2*x-1。

*/

sum2+=n-2*val[i]-1;

ans=min(ans,sum2);

}

printf("%d\n",ans);

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: