hdu1394-Minimum Inversion Number-线段树和逆序数的爱情

hdu1394-Minimum Inversion Number

题链:http://acm.hdu.edu.cn/showproblem.php?pid=1394

这道题目呢,怎么说,就是求给定的的n个数的逆序数,然后依次把前 i ( 0<i<n )个数放到序列后面,再求逆序数然后输出这n个序列的最小逆序数.

两个点: 

Point 1 : 如何根据前一个序列求下一个序列的逆序数. 如果好好找找规律会发现对于某个序列 L ,其逆序数为 K, 如果将第一个数 M 放到最后面 , 

                 那么其逆序数的变化就是 n - 1 -  2*M ( 减少为 M   增加为 n - 1 -M ).

Point 2 :如何算出一个序列的逆序数初始值——这个,就是线段树的用处了,对于 x而言,它的逆序数就等于区间【 0 , x+1 】的个数,不过注意求的时候要逆着往线段树里加数

( for ( i=Nn-1 ; i>=0 ; i-- ) ).


#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#define maxn 5005
int Nn , M ;
int num[maxn] ;
struct line
{
    int left,right ;
    int mx ;
}stu[maxn*4];

void buil ( int l , int r , int n )
{
    int mid ;
    stu[n].left = l ;
    stu[n].right = r ;
    stu[n].mx = 0 ;
    if( l == r ) {
        return ;
    }
    mid = ( l + r ) / 2 ;
    buil( l , mid , 2*n ) ;
    buil( mid+1 , r , 2*n+1 ) ;

}
void addsub( int i )
{
    int ll , rr , mm, n ;
    ll = 0 ;
    rr = Nn - 1 ;
    n = 1 ;
    while( ll != rr ){
        if( ll <= i && i <= rr ){
            stu[n].mx += 1 ;
            mm = ( ll + rr ) / 2 ;
            if( mm >= i ){
                rr = mm ;
                n = 2*n ;
            }
            else{
                ll = mm + 1 ;
                n = 2*n + 1 ;
            }
        }
    }
    stu[n].mx += 1 ;
}
int cha ( int A , int B , int n )
{
    int mid ;
    if( stu[n].left == A && stu[n].right == B ){
        return stu[n].mx ;
    }
    mid = ( stu[n].left + stu[n].right )/2 ;
    if( mid >= B ){
        return cha( A , B , 2*n );
    }
    else{
        if(mid < A){
            return cha ( A , B , 2*n+1 ) ;
        }
        else{
            return  cha( A , mid , 2*n ) + cha( mid + 1 , B , 2*n+1 ) ;
        }
    }
}
int main()
{
    int i , j , j_ ;
    while ( scanf( "%d" , &Nn ) != EOF ){
        buil( 0 , Nn - 1 , 1 ) ;
        j = 0 ;
        for ( i = 0 ; i < Nn ; i ++ ){
            scanf( "%d" , &num[i] ) ;
        }
        for ( i = Nn-1 ; i >= 0 ; i-- ){
            addsub( num[i] ) ;
            if( num[i] > 0 )
                j += cha( 0 , num[i]-1 , 1 ) ;
        }
        j_ = j ;
        for( i = 0 ; i < Nn-1 ; i++ ){
            j_  = j_ - 2*num[i] + Nn - 1 ;
            j = j < j_ ? j : j_ ;
        }
        printf( "%d\n" , j ) ;
    }
    return 0;
}

     
     
    
    
   
   

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值