HDU 1394 Minimum Inversion Number (树状数组)

时间:2022-02-05 03:15:08

题目链接

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

题意

给你n个数字 这些数字都是从0到n-1的,每次把第一个位置的数字移到最后一位形成一个新的序列,然后求出这些序列的逆序数,最后输出最小的逆序数。

分析

什么是逆序数:对于数x和y,若y的位置在x的前面并且y比x大,那么(x,y)就是一个逆序数对,

普通方法解决:例子:

5  4  3  6  1  2
当输入5时,就用一个标记数组a[5]=1,(a数组初始化全部为零),然后从6开始往后遍历,当遇到a[i]==1时,说明i这个数在6之前出现过,那么6的逆序数对就增加一个。
输入4时同理。。。。

这种方法很容易想到,代码也很容易实现。两个for循环就行了,但是套路那么深,这道题怎么可能怎么简单,肯定会超时的,就得用树状数组来做了。

设e[ ]就是这个树状数组,e数组是用来维护a数组的信息,e[i]代表是在当前情况下从1到i这个区间已经出现的数的个数,如 e[3]=2 代表的是在 [1,3]已经出现了两个数字 e[n]=k,代表的是在[1,n]已经出现的数的个数, e[n]-e[3]所代表的意思就很显然是此时大于3的已经出现的数的个数,这不就是3的逆序数嘛~~~我等凡夫俗子老半天才看懂大神的代码

由于这道题的数据时从0到n-1的,当求出一个序列的逆序数后,把这个序列的第一个数放到最后的位置形成的新的序列,这个新的序列的逆序数和原来的逆序数是由一定的关系的:

假设num[]数组如下  个数n=6
1 4 3 5 0 2 这个序列的逆序数为 x
4 3 5 0 2 1 当把1放到后面时,0的逆序数就减少了一个 但是比1 大的2 3 4 5 就成了1的逆序数
3 5 0 2 1 4 当把4放到后面时,3,2,1,0的逆序数就各减少了一个 但是比4 大的5 就成了4的逆序数
所以可推出如下规律: 新的逆序数t=x-num[1] + n-1-num[i]
x-num[1]: 把第一个数字放到后面,比num[1]小的数字的逆序数减少了,
+ n-1-num[i]:把第一个数字放到后面,比num[1]大的数字就构成num[1]的逆序数

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std; const int N=5010; int n,arr[N],num[N]; int lowbit(int x)
{
return x&(-x);
} void update(int id,int x)
{
while (id<=N)
{
arr[id]+=x;
id+=lowbit(id);
}
} int Sum(int id)
{
int ans=0;
while (id>0)
{
ans+=arr[id];
id-=lowbit(id);
}
return ans;
}
int main()
{
//freopen("1.txt","r",stdin);
while (scanf("%d",&n)!=EOF)
{
memset(arr,0,sizeof(arr));
int i,ans=0;
for (i=1; i<=n; i++)
{
scanf("%d",&num[i]);
ans+=Sum(n+1)-Sum(num[i]+1);//当前的逆序数个数
update(num[i]+1,1);
}
int tmp=ans;
for (i=1; i<=n; i++)
{
tmp+=n-1-num[i]-num[i];
ans=min(ans,tmp);
}
printf("%d\n",ans); }
return 0;
}