AcWing 107. 超快速排序(归并排序 + 逆序对 or 树状数组)
在这个问题中,您必须分析特定的排序算法----超快速排序。
该算法通过交换两个相邻的序列元素来处理n个不同整数的序列,直到序列按升序排序。
对于输入序列9 1 0 5 4
,超快速排序生成输出0 1 4 5 9
。
您的任务是确定超快速排序需要执行多少交换操作才能对给定的输入序列进行排序。
输入格式
输入包括一些测试用例。
每个测试用例的第一行输入整数n,代表该用例中输入序列的长度。
接下来n行每行输入一个整数aiai,代表用例中输入序列的具体数据,第i行的数据代表序列中第i个数。
当输入用例中包含的输入序列长度为0时,输入终止,该序列无需处理。
输出格式
对于每个需要处理的输入序列,输出一个整数op,代表对给定输入序列进行排序所需的最小交换操作数,每个整数占一行。
数据范围
0≤N<5000000≤N<500000,
0≤ai≤9999999990≤ai≤999999999
输入样例:
5
9
1
0
5
4
3
1
2
3
0
输出样例:
6
0
算法:归并排序 + 逆序对 or 树状数组
题解:求最少的交换次数,其实就是求当前这个序列的逆序数。
归并排序 + 逆序对:
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; const int maxn = 5e5+7; ll arr[maxn], b[maxn]; ll ans; void merge_sort(ll *arr, int l, int mid, int r) { int i = l, j = mid + 1; int k = 0; while(i <= mid || j <= r) { if(j > r || (i <= mid && arr[i] <= arr[j])) { b[k++] = arr[i++]; } else { ans += mid - i + 1; b[k++] = arr[j++]; } } for(int i = 0; i < k; i++) { arr[l + i] = b[i]; } } void merge(ll *arr, int l, int r) { if(l < r) { int mid = (l + r) >> 1; merge(arr, l, mid); merge(arr, mid + 1, r); merge_sort(arr, l, mid, r); } } int main() { int n; while(scanf("%d", &n) && n) { for(int i = 1; i <= n; i++) { scanf("%lld", &arr[i]); } ans = 0; merge(arr, 1, n); cout << ans << endl; } return 0; }
树状数组:
#include <iostream> #include <cstdio> #include <memory.h> #include <vector> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 5e5+7; vector<int> v; int arr[maxn]; ll tree[maxn << 2]; int size; int lowbit(int x) { return x & (-x); } int find(int x) { return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; } void update(int x, int val) { while(x <= size) { tree[x] += val; x += lowbit(x); } } ll getSum(int x) { //求出前面有多少个小于或等于x的数 ll res = 0; while(x > 0) { res += tree[x]; x -= lowbit(x); } return res; } int main() { int n; while(scanf("%d", &n) && n) { memset(tree, 0, sizeof tree); for(int i = 1; i <= n; i++) { scanf("%d", &arr[i]); v.push_back(arr[i]); } sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); size = v.size(); ll ans = 0; for(int i = 1; i <= n; i++) { update(find(arr[i]), 1); ans += i - getSum(find(arr[i])); //用总数减去小于或等于arr[i]的数,就是当前这个数的逆序数 } cout << ans << endl; } return 0; }