离散化+树状数组 求逆序对数

除了归并排序,也可以用树状数组求逆序对数。先看 POJ 2299
Ultra-QuickSort
Time Limit: 7000MS   Memory Limit: 65536K
Total Submissions: 32606   Accepted: 11633

Description

In this problem, you have to analyze a particular sortingalgorithm. The algorithm processes a sequence of n distinctintegers by swapping two adjacent sequence elements until thesequence is sorted in ascending order. For the inputsequence

9 1 0 5 4 ,
Ultra-QuickSort produces the output
0 1 4 5 9 .
Your task is to determine how many swap operations Ultra-QuickSortneeds to perform in order to sort a given input sequence.

Input

The input contains several test cases. Every testcase begins with a line that contains a single integer n< 500,000 -- the length of the input sequence. Eachof the the following n lines contains a single integer 0 ≤ a[i] ≤999,999,999, the i-th input sequence element. Input is terminatedby a sequence of length n = 0. This sequence must not beprocessed.

Output

For every input sequence, your program prints asingle line containing an integer number op, the minimum number ofswap operations necessary to sort the given input sequence.

Sample Input

5
9
1
0
5
4
3
1
2
3
0

Sample Output

6
0
 
这里面a[i]的范围是0到999,999,999,太大了,所以要先用离散化缩小范围提高效率(就跟上次那个map的映射差不多,不需要记下来每个数是多大,只要把它排第几那个位置记下来就行了,不影响求逆序对数)。离散化的方法可以是先用结构体记下这个数的大小和初始位置,再对大小进行排序(初始位置跟着大小一起排了),由此得到一个大小有序的数列,同时知道每个数的初始位置。然后用一个数组就可以记住原来每个数的大小应该排在的位置了。假设用b这个数组存,node结构体表示数(node.v是值,node.index是这个数的位置),则排序后如果这个数和上个数不相等,有b[node[i].index]=i,如果相等就是b[node[i].index]=b[node[i-1].index]。这样离散化完成了。

  那什么是树状数组呢?百度百科上的图 离散化+树状数组 求逆序对数_第1张图片

  这个貌似有点不好理解,反正记得这些性质:
1.设每个数二进制末尾0的个数是k,则每个数管的数个数就是2的k次方,并且管的最后一个就是自己那个数。比如8的2进制是1000,有3个0,2的3次方是8,则c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]=a[4]+a[6]+a[7]+a[8]。所以用这种方法求和的话就只用算几个点,不用一个一个加了,节省时间。
2.这个数加上这个数管的个数等于下个管他的数。
  怎么算k呢?下面是百度来的=。= :

算这个2^k有一个快捷的办法,定义一个函数如下即可:
int lowbit(int x){
return x&(x^(x–1));
}
利用机器补码特性,也可以写成:
int lowbit(int x){
return x&(-x);
}

这两个是一样的,^是其中一个是真一个是假就是真,&是两个都真才是真,第一个我也不会解释 离散化+树状数组 <wbr>求逆序对数,第二个负数就是取反,加1,比如2是10,取反是01,加1是10,10&10是10,就是2,x&(-x)就是k的值,这个还是很好理解的。。

  然后就可以用这个性质构造树状数组了~按照刚才映射的b数组,每加入一个数就计算此时已经加入的比它大的数的个数,也就是c数组里面加入它的位置的后面有多少个已经存在的数。这也等于总共多少个减去前面有多少个(包括它自己)~
  构造树状数组的方法:假设要把c[i]加1,不光c[i]要加1,管他的那些都要加1。这个数加完1后把这个数加上这个数管的数的个数就是下一个要加1的数。貌似有点绕~哦呵呵。。 离散化+树状数组 <wbr>求逆序对数

代码来咯~(有百度参考别人的)
#include<stdio.h>
#include<string.h>
int b[500010],c[500010],N;
typedef struct NNode
{
    int index,v;
} Node;
Node node[500010];
int cmp(const void *a,const void *b)
{
    return (*((Node *)a)).v-(*((Node *)b)).v;
}
int lowbit(int x)  //算k
{
    return x&(-x);
}
void add(int i,int v)  //构造树状数组
{
    while(i<=N)
    {
        c[i]+=v;
        i+=lowbit(i);
    }
}
int sum(int i) //算前面已经有多少个数(包括它自己)
{
    int s=0;
    while(i>0)
    {
        s+=c[i];
        i-=lowbit(i);
    }
    return s;
}
int main()
{
    freopen("D:\\CodeBlocks\\file\\in.txt","r",stdin);
    while(scanf("%d",&N),N)
    {
        int i;
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        for(i=1; i<=N; i++)
        {
            scanf("%d",&node[i].v);
            node[i].index=i;
        }
        qsort(node+1,N,sizeof(node[1]),cmp); //离散化
        b[node[1].index]=1;
        for(i=2; i<=N; i++)
            if(node[i].v!=node[i-1].v)b[node[i].index]=i;
            else b[node[i].index]=b[node[i-1].index];
        long long ans=0;
        for(i=1; i<=N; i++)
        {
            add(b[i],1);
            ans+=sum(N)-sum(b[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}



你可能感兴趣的:(离散化+树状数组 求逆序对数)