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;
}