BIT(树状数组)——求逆序对数

既然上次我写了一个归并排序求逆序对的方法,而且以前也写过一个BIT基础的教程,那么这次我们就来讲一下逆序对的另一种求法——使用BIT求解逆序对数。

逆序对是这样定义的:如果i < j 并且ai > aj 那么这就叫一个逆序对。不难看出,其实我们可以用离散化的方法来做。但是离散化后,就一定要使用BIT去求解了。

我们的BIT的基础函数还是不变的,但是要操作别的东西达到我们求解逆序对数的目的:

1 首先对于将数字离散化,这个我们是需要用到排序的,我们需要使用数字的大小来记录这个数本来在第几位,以及这个数在求逆序对之前应该在第几位,那我们就要用到algorithm库中的sort函数(Pascal只能手写函数,对此我表示我很同情学P的同学们),相信大家也应该会使用struct,所以呢,这里附上代码,毕竟代码才是真理。

 1 struct edge{
 2     int v,pos;
 3 }in[100010]; // 定义
 4 bool cmp(edge a,edge b){
 5     return a.v < b.v;
 6 }
 7 
 8     for(int i = 1;i <= n;++i){
 9         cin >> in[i].v;
10         in[i].pos = i;
11     }
12     sort(in+1,in+n+1,cmp);
View Code

2. 对于离散化后的数据(按照顺序保存),我们要对其进行操作,加装一个新的数组(按位置保存的数组),然后算每个数据在整个数据中应该排第几位(注意,题目有可能出现ai = aj,所以要手工计算,而不能排完序就一撒手,然后记录下来原先在哪个位置的数据现在在那个位置,需要重新进行循环计算,看看这个数据到底在哪个位置上);

3.以上操作完成以后,我们的记录工作便完成了,接下来就是使用BIT来计算了:执行从1到n的循环,对于每一个aa[i]执行加一操作,不记录在aa数组中,只在c数组中进行计算,然后接着ans要加上i-query(aa[i])(这是关于原先第i位的数的逆序对的个数,query(aa[i])是指在i之前比a小的数的个数,此处比较难懂,请读者自己想一想,因为我也是说不清楚到底过程是怎样的,但是我个人认为还是自己思考的要好一些),之后累加在一个ANS变量中,ANS最终就是我们所求的逆序对数

现在我们来说一下这个算法的时间复杂度上的问题,事实上这个代码的时间复杂度和归并排序求逆序对数是相同的都是O(nlogn)。因为每次BIT的基本操作的时间复杂度都是O(logn),然后我们在主程序中循环求解中循环了n次,所以时间复杂度就相当于O(nlogn),这个代码比较好写,但是就是很难懂,如果大家习惯了归并排序求逆序对的操作后,这个方法我建议涉猎一下,但是还是不要写自己拿不稳的程序,以免程序出错。

代码才是真理,一切嘴上说的都是空虚的,附上代码——(建议大家先背代码,然后再去逐步理解,毕竟代码很难懂,我自己都想了1个月左右才刚刚有点理解):

 1 #include <iostream>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define lowbit(x) x&-x
 5 using namespace std;
 6 int n;
 7 long long ans;
 8 int c[100010];
 9 struct edge{
10     int v,pos;
11 }in[100010];
12 bool cmp(edge a,edge b){
13     return a.v < b.v;
14 }
15 long long aa[100010];
16 int query(int x){
17     long long ret = 0;
18     while(x > 0){
19         ret += c[x];
20         x -= lowbit(x);
21     }
22     return ret;
23 }
24 void add(int x,int d){
25     while(x <= n){
26         c[x] += d;
27         x += lowbit(x);
28     }
29 }
30 int main(){
31     cin >> n;
32     ans = 0;
33     memset(aa,0,sizeof(aa));
34     memset(c,0,sizeof(c));
35     for(int i = 1;i <= n;++i){
36         cin >> in[i].v;
37         in[i].pos = i;
38     }
39     sort(in+1,in+n+1,cmp);
40     int reflect = 0;
41     for(int i = 1;i <= n;++i){
42         if(in[i].v != in[i-1].v)reflect++;
43         aa[in[i].pos] = reflect;
44     }
45     for(int i = 1;i <= n;++i){
46         add(aa[i],1);
47         ans += i - query(aa[i]);
48     }
49     cout << ans << endl;
50     return 0;
51 }
View Code
如果有读者看不懂其中的ADD和QUERY函数的,请去我的博客中看树状数组的基础讲解。这次的讲解就到这里,NOIP又来了,祝同学们能考一个好成绩吧。
posted @ 2015-11-05 20:39  小钢钉丶coding  阅读(1176)  评论(0)    收藏  举报