今天刚学了个用线段树求逆序数个数的方法。这个序列是0,1,2,3,4......,n 的,但是是无序的。
基本思路:
首先根据元素个数来建立一颗空树。初始时,所有节点的 sum 值都为 0,假定这个序列初始为空,然后一个数一个数的遍历这个序列,每遍历一个,都相当与在这个序列中插入了一个数,查找出在它前面而比它大的数的个数,方法是查找出线段树中 这个数到最大数这个区间之间的 sum 值,也就相当于线段树中的查询。插入了一个数之后,序列就多了一个数,所有包含插入的数区间的 sum 值都要加 1,以便于小于它的数询问。
#include<stdio.h>
#define MAX 20000
struct node{
int l;
int r;
int sum;
}nodes[ MAX ];
int A[ MAX ];
void build(int l, int r, int i){
nodes[i].l=l;
nodes[i].r=r;
if(l == r) {
nodes[i].sum=0;
return;
}
build(l,(l + r)/2,i<<1);
build(((l + r)/2)+1, r, (i<<1)+1);
nodes[i].sum=0;
}
int add(int l, int r, int i){
int mid=0;
if(nodes[i].l==l && nodes[i].r==r)
return nodes[i].sum;
mid = (nodes[i].l + nodes[i].r)>>1;
if(mid>=r) return add(l, r, i<<1);
else if(mid<l) return add(l, r, (i<<1) + 1);
else return add(l,mid,i<<1) + add(mid+1,r,(i<<1)+1);
}
void update(int x, int i ){
int mid;
nodes[i].sum+=1;
if(nodes[i].l == nodes[i].r)
return;
mid = (nodes[i].l + nodes[i].r)>>1;
if( x<=mid ) update(x,i<<1);
else update(x,(i<<1)+1);
}
int main(){
int n,m,i;
int _sum;
int min = 0;
while(scanf("%d",&n) != EOF){
_sum=0;
build(0,n-1,1);
for(i=0;i<n;i++){
scanf("%d",&A[i]);
}
for(i=0;i<n;i++){//记录初始序列的逆序数
_sum += add(A[i],n-1,1);//当前数到最大数之间有多少在它的前面
update(A[i],1);
}
printf("%d\n",_sum);
}
}