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

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

输入输出格式

输入格式:

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。

输出格式:

给定序列中逆序对的数目。

输入输出样例

输入样例#1:
6
5 4 2 6 3 1
输出样例#1:
11

说明

对于50%的数据,n≤2500

对于100%的数据,n≤40000。

代码:解法一

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 int N,a[100005],temp[100005],cnt;
 7 
 8 void print(){
 9     for(int i=1;i<=6;i++){
10         printf("%d ",a[i]);
11     }
12     puts("");
13 }
14 
15 void merge_sort(int l,int r){
16     if(l>=r) return;
17     int mid=(l+r)>>1;
18     merge_sort(l,mid);merge_sort(mid+1,r);
19     
20     
21     int i=l,j=mid+1,point=l;
22     while(i<=mid&&j<=r){
23         if(a[i]>a[j]){
24             temp[point++]=a[j++];
25             cnt+=(mid-i+1);
26         }
27         else{
28             temp[point++]=a[i++];
29         }
30     }
31     
32     while(i<=mid) temp[point++]=a[i++];
33     while(j<=r)   temp[point++]=a[j++];
34     
35     for(int k=l;k<=r;++k)
36         a[k]=temp[k];
37 //    print();
38 }
39 
40 int main(){
41 //    freopen("01.txt","r",stdin);
42     
43     scanf("%d",&N);
44     for(int i=1;i<=N;++i)
45         scanf("%d",&a[i]);
46     
47     merge_sort(1,N);
48     
49     printf("%d
",cnt);
50     return 0;
51 }

暴力枚举的话,可以过两个点~

转载题解如下:http://blog.csdn.net/acdreamers/article/details/16849761

归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。

在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在

前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并

排序中的合并过程中计算逆序数.

解法二:树状数组+离散化

 1 #include <iostream>  
 2 #include <cstring>  
 3 #include <cstdio>  
 4 #include <algorithm>  
 5 using namespace std;  
 6   
 7 const int N = 500005;  
 8   
 9 struct Node  
10 {  
11     int val;  
12     int pos;  
13 };  
14   
15 Node node[N];  
16 int c[N], reflect[N], n;  
17   
18 bool cmp(const Node& a, const Node& b)  
19 {  
20     return a.val < b.val;  
21 }  
22   
23 int lowbit(int x)  
24 {  
25     return x & (-x);  
26 }  
27   
28 void update(int x)  
29 {  
30     while (x <= n)  
31     {  
32         c[x] += 1;  
33         x += lowbit(x);  
34     }  
35 }  
36   
37 int getsum(int x)  
38 {  
39     int sum = 0;  
40     while (x > 0)  
41     {  
42         sum += c[x];  
43         x -= lowbit(x);  
44     }  
45     return sum;  
46 }  
47   
48 int main()  
49 {  
50     while (scanf("%d", &n) != EOF && n)  
51     {  
52         for (int i = 1; i <= n; ++i)   
53         {  
54             scanf("%d", &node[i].val);  
55             node[i].pos = i;  
56         }  
57         sort(node + 1, node + n + 1, cmp);   //排序  
58         for (int i = 1; i <= n; ++i) reflect[node[i].pos] = i;   //离散化  
59         for (int i = 1; i <= n; ++i) c[i] = 0;   //初始化树状数组  
60         long long ans = 0;  
61         for (int i = 1; i <= n; ++i)  
62         {  
63             update(reflect[i]);  
64             ans += i - getsum(reflect[i]);  
65         }  
66         printf("%lld
", ans);  
67     }   
68     return 0;  
69 }  

转载自http://blog.csdn.net/alongela/article/details/8142965

给定n个数,要求这些数构成的逆序对的个数。除了用归并排序来求逆序对个数,还可以使用树状数组来求解。

树状数组求解的思路:开一个能大小为这些数的最大值的树状数组,并全部置0。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。

题目中的数都是独一无二的,这些数最大值不超过999999999,但n最大只是500000。如果采用上面的思想,必然会导致空间的巨大浪费,而且由于内存的限制,我们也不可能开辟这么大的数组。因此可以采用一种称为“离散化”的方式,把原始的数映射为1-n一共n个数,这样就只需要500000个int类型的空间。

离散化的方式:

struct Node

{

int val;

int pos;

};

Node node[500005];

int reflect[500005];

val存放原数组的元素,pos存放原始位置,即node[i].pos = i。

把这些结构体按照val的大小排序。

reflect数组存放离散化后的值,即reflect[node[i].pos] = i。

这样从头到尾读入reflect数组中的元素,即可以保持原来的大小关系,又可以节省大部分空间。

这个可以辅助理解

 http://blog.csdn.net/cattycat/article/details/5640838

树状数组实际上还是一个数组,只不过它的每个元素保存了跟原来数组的一些元素相关的结合值。

若A为原数组,定义数组C为树状数组。C数组中元素C[ i ]表示A[ i –lowbit( i ) + 1]至A[ i ]的结合值。

lowbit(i)是i的二进制中最后一个不为零的位数的2次方,可以这样计算

lowbit(i)=x&(-x)

lowbit(i)=x&(x^(x-1))

版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!
原文地址:https://www.cnblogs.com/radiumlrb/p/5811757.html