poj 2299 Ultra-QuickSort(归并排序/树状数组/线段树)
题目链接:http://poj.org/problem?id=2299
题意
给出含有 $n$ 个不同的数的数组,相邻元素间可以两两交换,计算数组变为升序需要交换多少次。
题解
其实就是计算数组中的逆序数。
题解一(归并排序)
考虑两个区间合并的过程,如果左边区间未归并数首部的值大于右边区间未归并数首部的值(以下简称左首部、右首部),那么左首部及其之后的数都与右首部构成逆序。
代码
#include <cstdio> #define ll long long const int N = 5e5 + 100; int n, a[N], b[N]; ll ans; void merge(int start, int mid, int end) { int i = start, j = mid + 1, k = start; while (i <= mid && j <= end) { if (a[i] <= a[j]) b[k++] = a[i++]; else { ans += mid - i + 1; //原写法为 j - k,感觉 mid - i + 1 更好理解些 b[k++] = a[j++]; } } while (i <= mid) b[k++] = a[i++]; while (j <= end) b[k++] = a[j++]; for (int i = start; i <= end; i++) a[i] = b[i]; } void MergeSort(int l, int r) { if (l >= r) return; int mid = (l + r) / 2; MergeSort(l, mid); MergeSort(mid + 1, r); merge(l, mid, r); } int main() { while (scanf("%d", &n) && n != 0) { ans = 0; for (int i = 0; i < n; i++) scanf("%d", &a[i]); MergeSort(0, n - 1); printf("%lld\n", ans); } }
题解二(树状数组)
假设每个数的位置都有 $1$ 盏灯,从小到大熄灭每个数的灯,那么每个数的逆序数即为访问它时它的左边仍亮着的灯的个数。问题即化为单点修改,区间求和。
代码
#include <cstdio> #include <utility> // pair #include <algorithm> //sort #define lowbit(x) (x & (-x)) #define ll long long using namespace std; const int N = 5e5 + 100; int n, bit[N]; pair<int, int> pr[N]; void update(int pos, int val) { for (int i = pos; i <= N; i += lowbit(i)) bit[i] += val; } int query(int pos) { int res = 0; for (int i = pos; i >= 1; i -= lowbit(i)) res += bit[i]; return res; } int main() { while (scanf("%d", &n) && n != 0) { for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); pr[i] = {x, i}; } sort(pr + 1, pr + 1 + n); for (int i = 1; i <= n; i++) bit[i] = lowbit(i); //开始时每个位置的灯都为 1,所以 bit[i] 的值为它所管辖的位置的个数,等价于执行 n 次 update(i, 1) ll ans = 0; for (int i = 1; i <= n; i++) { update(pr[i].second, -1); ans += query(pr[i].second - 1); } printf("%lld\n", ans); } }
拓展
可以考虑下如何用线段树实现,以及当数组需要交换为降序时代码应如何改动。
参考资料
https://www.runoob.com/w3cnote/merge-sort.html(归并排序过程的动图演示超赞)
https://blog.csdn.net/morewindows/article/details/6678165/(白话经典算法系列超赞)
https://www.cnblogs.com/handsomecui/p/4804070.html(归并排序)
https://blog.csdn.net/jk_chen_acmer/article/details/79347003(逆序数、树状数组)