题目地址
使用离散化和树状数组的代码

题目描述:

riba2534不小心穿越到了异世界,他必须从异世界出来,但是异世界有一个大魔王,非得让riba2534回答出他的问题才让他回到地球,问题是这样的:

大魔王用魔法变出来了n座大山,并且告诉你这n座大山的高度,现在他想问你,对于给出的每一座大山,这座大山之前有多少座大山比他的高度低。

riba2534非常惊慌失措,所以请你救救他!

输入描述:

多组测试数据 第一行给出一个整数n(1<=n<=100000)代表有n座大山
第二行给出来每座大山的高度h(1<=h<=1000000000),以空格作为分割。

输出描述:

对于每组数据,输出一行。 代表每座大山之前有多少座大山比这座大山低

思路

在这里就不介绍采用分治发的归并排序了,大家可以去这里看“点击”,刚开始做这题用了树状数组和离散化才把它A了,后来看了最优代码是用的是分治之后,打算写一篇采用分治的解法。(假定看这篇博客时各位已经掌握了归并排序)。
首先,由于排序后会改变它原先的位置(除了个别已经恰好排好了的),所以要记录他们原先在数组中的位置,也就是使用了一个结构体(由于只有两个信息,所以我使用了结构体模板pair)

const int N = 1e5+1;
pair<int , int>a[N], temp[N];
int num[N];
temp数组用于做中间变量,知道归并排序的都懂!
num数组储存结果

刚开始的时候我采用的是从小到大排序的算法,发现会有好多情况需要考虑,然后换成从大到小的方法就很容易理解了。
还是举个例子吧,来个理想化的
3 2 1 6 5 4
由于我们是按逆序排的,所以最后要合并的两个区间的是(3,2,1) 和(6,5,4)并且现在这两个区间内每个数的num值都为0,然后我们来进行合并操作。

void merga(int l, int mid, int r) {
  int i = l, j = mid + 1, k = l;
  while(i <= mid && j <= r) { 
    if(a[i].first >= a[j].first) { //first是值, second是每个数最开始时的位置。
      temp[k++] = a[i++];
    } 
    else { 
      num[a[j].second] += mid + 1 - i;
      temp[k++] = a[j++];
    } 
  } 
  while(i <= mid) temp[k++] = a[i++];
  while(j <= r) temp[k++] = a[j++];
  for (int i = l; i <= r; i++) { 
    a[i] = temp[i];
  } 
} 

我来模拟一下他增加的操作,
3 < 6, num[3] (6在原数组中下表为3,别搞混了) += 2 + 1 - 0
3 < 5, num[4] += 2 + 1 - 0;
3 < 4, num[5] += 2 + 1 - 0;
最后合并。
也就是说它现在就是在找左边有多少个比右边的小,为什么是+=呢,实际上也很简单,如果我们把我刚刚的例子当成是另一个更长的数组的子串,那么再进行合并的时候就得加上上一次合并的结果。
给出完整代码:

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5+1;
pair<int , int>a[N], temp[N];
int num[N];
void merga(int l, int mid, int r) {
  int i = l, j = mid + 1, k = l;
  while(i <= mid && j <= r) { 
    if(a[i].first >= a[j].first) { 
      temp[k++] = a[i++];
    } 
    else { 
      num[a[j].second] += mid + 1 - i;
      temp[k++] = a[j++];
    } 
  } 
  while(i <= mid) temp[k++] = a[i++];
  while(j <= r) temp[k++] = a[j++];
  for (int i = l; i <= r; i++) { 
    a[i] = temp[i];
  } 
} 
void merga_sort(int l, int r) { 
  if(l < r) {
    int mid = (l + r) / 2;
    merga_sort(l, mid);
    merga_sort(mid + 1, r);
    merga(l, mid, r);
  } 
} 
int main() { 
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n;
	while(cin >> n) { 
	  memset(num, 0, sizeof(num));
		for (int i = 0; i < n; i++) { 
			cin >> a[i].first;
			a[i].second = i;
		} 
		merga_sort(0, n-1);
		for (int i = 0; i < n; i++) { 
			cout << num[i];
			if(i < n - 1)
				cout << " ";
			else cout << endl;
		} 
	} 
} 

萌新表达可以不太准确,有什么不懂的可以在下面评论。若有哪里不正确,请各位大佬指正。