HDU1394:Minimum Inversion Number(线段树单点更新)

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


 

题意:通过循环移位找到最小的逆序数

由于序列中的数字只有0~n-1,所以将队头元素k移到末尾逆序数的总和必定减少k-1个,并且增加n-k个逆序数,所以只需要求一次逆序数,其他逆序数可以通过这个方法求得

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int n;
int s[5005];

struct node
{
    int l,r,n;
} a[1000000];


void init()
{
    int i,k,j;
    for(k = 1; k<n; k<<=1);
    for(i = k; i<2*k; i++)
    {
        a[i].l = a[i].r = i-k+1;
        a[i].n = 0;
    }
    for(i = k-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 i,minn,sum;
    while(~scanf("%d",&n))
    {
        init();
        sum = 0;
        for(i = 0; i<n; i++)
        {
            scanf("%d",&s[i]);
            s[i]++;
            insert(1,s[i]);
            sum+=find(s[i]+1,n,1);
        }
        minn = sum;
        for(i = 0; i<n-1; i++)
        {
            sum-=s[i]-1;//将序列第一个一道末尾逆序数减少的个数为序列后面比这个数小的个数
            sum+=n-s[i];//逆序数增加的个数为序列序列中比这个数大的个数
            if(minn>sum)
                minn = sum;
        }
        printf("%d\n",minn);
    }

    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值