HDU1394 Minimum Inversion Number【逆序数】

Minimum Inversion Number

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


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

 

题目大意:

给你一个序列 a1,a2……an,计算出该序列的逆序数,将第一个数a1移到序列末尾,变为a2,a3……an,a1计算出逆序数。之后再将第一个数a2移到序列末尾,计算出逆序数。依次进行移位,计算逆序数,直到序列变为an,a1,a2,……an-1

找出上述所有序列中最小的逆序数。

思路:

刚看题目的时候,不知道要怎么计算逆序数。参考了HH大神的代码才明白。

先建立一个0-N-1的线段树,代表0-N-1个数的区间,按最初序列从前往后的顺序向线段树中插入数值,比如数值为3就插入到线段树中叶子节点为[3,3]的节点上,节点赋值为1,每插入一次a[i],计算一下a[i]+1到N-1区间和,代表着a[i]的逆序数,具体可自己画一下图表就明白了,a[i]+1到N-1的区间和,代表着之前插入的数值有多少个比a[i]大的,也就是a[i]的逆序数。

将a0-aN-1的逆序数加起来就是最初序列的逆序数。

之后,进行移位的逆序数运算。因为我们已经得到了最初序列的逆序数,就可以根据一步运算得到一次移位后的逆序数,所以就不用再重新建立线段树,计算移位后的逆序数了。

那么将第一个数移到最后,逆序数应该增加或减少多少呢?

假设第一个数为a[i],因为是0~N-1的数,那么a[i]后边有(N-a[i]-1)个比a[i]大的数,有a[i]个比a[i]小的数,移位后,逆序数前者增加N-a[i]-1个,后者减少a[i]个。则逆序数总共增加N-a[i]-1-a[i],最后将a[i]遍历一下,找到最小的逆序数即可。

# include<iostream>
# include<algorithm>
using namespace std;
const int MAXN = 5010;

int sum[MAXN<<2];
void pushup(int root)
{
    sum[root] = sum[root<<1] + sum[root<<1|1];
}

void build(int root,int L,int R)
{
    sum[root] = 0;
    if(L==R)
        return;
    int mid = (L+R)>>1;
    build(root<<1,L,mid);
    build(root<<1|1,mid+1,R);

}

void updata(int root,int L,int R,int i,int v)
{
    if(L==R)
    {
        sum[root]++;
        return;
    }
    int mid = (L+R)>>1;
    if(i <= mid)
        updata(root<<1,L,mid,i,v);
    else
        updata(root<<1|1,mid+1,R,i,v);

    pushup(root);
}

int query(int root,int L,int R,int s,int e)
{
    if(s==L && e==R)
        return sum[root];
    int mid = (L+R)>>1;
    int res = 0;
    if(e <= mid)
        res += query(root<<1,L,mid,s,e);
    else if(s > mid)
        res += query(root<<1|1,mid+1,R,s,e);
    else
    {
        res += query(root<<1,L,mid,s,mid);
        res += query(root<<1|1,mid+1,R,mid+1,e);
    }
    return res;
}

int x[MAXN];

int main()
{
    int n;
    while(~scanf("%d", &n))
    {
        build(1,0,n-1);
        int sum = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &x[i]);
            sum += query(1,0,n-1,x[i],n-1);
            updata(1,0,n-1,x[i],1);
        }

        int res = sum;
        for(int i = 0; i < n; i++)
        {
            sum += n - x[i] -x[i] -1;
            res = min(res, sum);
        }
        printf("%d\n",res);
    }

    return 0;
}


 

 

 

你可能感兴趣的:(HDU1394 Minimum Inversion Number【逆序数】)