逆序对 (树状数组 | | 归并排序

Posted -ifrush

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆序对 (树状数组 | | 归并排序相关的知识,希望对你有一定的参考价值。

数组前面的一个元素 大于等于 后面的一个元素就是一个逆序对;

树状数组可以快速求前缀和,利用这一特性,可以求逆序对个数,见下:

用数组c[ i ]记录数组a[ n ]中i这一元素出现的次数 ,当a[ n ]中元素较大时可以离散化处理。

将a[ n ]从a[n -1]到a[0] 依次存到树状数组中,每存一个,对存的元素i求一次c[i]的前缀和, 这就是当前已扫描过的比i小的元素的个数,由于a[n]是倒着扫描的,所以此时比i小的元素都对应一个逆序对,统计之。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int c[600000];

int a[600000] ,b[600000];
int n;
long long ans;


int lowbit( int x){
    return x & ( -x);
}

void updata( int x ,int v){
     while( x<=n){
         c[x] += v;
         x += lowbit( x);
     }
}

int getsum( int x){
     int s=0;
     while( x>0){
         s += c[ x];
         x -= lowbit( x);
     }
     return s;
}

int main( ){
     while( ~scanf( "%d" ,&n) ,n){
         memset( c ,0 ,sizeof( c ));
         for( int i=0 ;i<n ;i++){
             scanf( "%d" ,&a[i]);
             b[i] = a[i];
         }
         sort( b ,b + n);                        //离散化处理
         int sz = unique( b ,b+n) - b;
         ans = 0;
         for( int i=n-1 ;i >=0  ;i--){
             int t = lower_bound( b ,b+sz ,a[i]) -b +1;  //搜索对应的元素下标
             ans += getsum( t) ;
             updata( t, 1);
         }
         printf( "%lld
" ,ans);
     }
     return 0;
}

归并排序求逆序对在lrj的高效算法设计一章学过,不过又把一些细节忘了:

1) 分治的参数x,y是左闭右开的,就是(n-1是数组末项下标 (0,n)->( 0,m ),(m ,n )

2)  分治条件y-x>1,因为y是开的,碰不到,y-x==1就是只有一个元素不能再分的情况了

3) 求逆序对时是 cnt += m-p,有半部分有一个小的左半部分没排的(m-p个)都是比他大的

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;

int a[600000];
int b[600000];
int n;
long long ans;

void ergesort( int x ,int y){
   //  cout << x<<‘ ‘<<y<<endl;
     if( y - x <= 1)return;
     int m = x + (y - x)/2;
     ergesort( x ,m) ;
     ergesort( m ,y) ;
     int p = x, q =m ,i=x;

     while ( p < m || q < y){
         if( q >=y || p<m && a[p] < a[q]) b[i++] = a[p++];
         else b[i++] = a[q++] ,ans+= m-p;
     }
     for( int i=x ; i<y ;i++) a[ i] = b[ i];
     return ;
}

int main( ){
     while( ~scanf( "%d" ,&n) ,n){
            ans=0;
         for( int i=0 ;i<n ;i++){
            scanf( "%d" ,&a[i]);
         }
         ergesort( 0 ,n);
  //   for( int i=0 ;i<n ;i++)printf("%d ",a[i]);
         printf( "%lld
",ans);
     }
     return 0;
}

 

以上是关于逆序对 (树状数组 | | 归并排序的主要内容,如果未能解决你的问题,请参考以下文章

逆序对 (树状数组 | | 归并排序

树状数组求逆序对

POJ - 2299 - Ultra-QuickSort = 归并排序 + 逆序对 / 树状数组

模板逆序队(树状数组/归并排序)

用归并排序或树状数组求逆序对数量 poj2299

洛谷 P1908 逆序对 Label:归并排序||树状数组