一天一道算法题——逆序对(归并排序,树状数组,离散化)

题目【逆序对】:https://www.luogu.com.cn/problem/P1908

解法一:暴力法,适用于n比较小的时候,本题不适用。

解法二:归并排序。

我们很容易得出在合并操作的时候会找到逆序对。

记i指向左边区域的当前节点,j指向右边区域的当前节点。

当a[i]<=a[j]时,a[i]与右边区域的大于等于a[j]的所有数不构成逆序对,只需i++就好。

当a[i]>a[j]时,a[j]与左区域的大于等于a[i]的所有数皆构成逆序对,所以逆序对数目+=mid-l+1

因为int会溢出,所以sum用了long long

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<algorithm>
 4 #define N 500010
 5 using namespace std;
 6 
 7 int n, a[N],c[N];
 8 long long sum=0;
 9 void mergeSort(int l, int r) {
10     if (l >= r)return;
11     int mid = (l + r) >> 1,k = mid + 1, t = l, left = l, right = r;
12     mergeSort(l, mid);
13     mergeSort(mid + 1, r);
14     while (l <= mid && k <= r) {
15         if (a[l] <= a[k]) {
16             c[t++] = a[l++];
17         }
18         else
19             c[t++] = a[k++], sum += (long long)mid - l + 1;
20     }
21     while (l <= mid)
22         c[t++] = a[l++];
23     while (k <= r)
24         c[t++] = a[k++];
25     for (; left <= right; left++)
26         a[left] = c[left];
27 }
28 
29 int main() {
30     scanf("%d", &n);
31     for (int i = 1; i <= n; i++)
32         scanf("%d", &a[i]);
33     mergeSort(1, n);
34     printf("%lld", sum);
35     return 0;
36 }

∠(°ゝ°)!

解法三:树状数组

记a[i]为原数组

然后我们将每个a[i]在树状数组tree[]里对应的位置都+1,这样a[i]的前缀和就代表了比它小或相等的数,那么i-前缀和就是逆序对的数量了。

接着……溢出了。

因为序列中的每个数不超过10^9,所以,你懂的。

为了减少空间消耗,我们采用离散化。

将a排序,然后将每个数分别用1……n代替,这样tree只需要10^5<<2的大小,大大减少了消耗。

代码如下:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<stdio.h>
 4 #define N 500010
 5 #define lowbit(x) x&-x
 6 typedef long long ll;
 7 using namespace std;
 8 
 9 int n, b[N],tree[N<<2];
10 struct node {
11     int val, num;
12 } a[N];
13 
14 //inline作用和define差不多
15 inline bool cmp(node a, node b) {
16     if (a.val == b.val)return a.num < b.num;
17     return a.val < b.val;
18 }
19 void insert(int x, int k) {
20     while (x <= n) {
21         tree[x] += k;
22         x += lowbit(x);
23     }
24 }
25 ll query(int x) {
26     ll ans = 0;
27     while (x) {
28         ans += tree[x];
29         x -= lowbit(x);
30     }
31     return ans;
32 }
33 
34 int main() {
35     scanf("%d", &n);
36     for (int i = 1; i <= n; i++)
37         scanf("%d", &a[i].val), a[i].num = i;
38     //离散化,防止有些数太大,导出空间溢出
39     sort(a + 1, a + n + 1, cmp);
40     for (int i = 1; i <= n; i++)
41         b[a[i].num] = i;
42     ll ans=0;
43     for (int i = 1; i <= n; i++) {
44         insert(b[i], 1);
45         ans += i-query(b[i]);
46     }
47     printf("%lld", ans);
48     return 0;
49 }

 

posted @ 2020-04-01 12:36  团子好软  阅读(175)  评论(0编辑  收藏  举报