题目链接:POJ 2299 Ultra-QuickSort
乍一看是冒泡排序,不过用冒泡排序会超时。
仔细想想其实就是求逆序对总数。
用分治法(归并排序改一下)可以做。
还可以用树状数组做,不过0 ≤ a[i] ≤ 999,999,999范围太大了,这里用到了离散化的思想,下面引用别人的解释:离散化,是数据范围太大是所借用的利器,举个例子,有四个数99999999 1 123 1583 数据范围太大,而树状数组中的c数组开的范围是数据的范围,这时候就需要离散化,把四个数一次标号为1 2 3 4(即第一个数,第二个数。。。),按键值排序之后 依次为2 3 4 1(即从小到大排序为第二个数,第三个数。。。),所以,第二个数是最小的,即f[2]=1,f[3]=2,f[4]=3,f[1]=4,也就是把键值变为了1~n,相对大小还是不变的,即4 1 2 3。比如要求原来四个数的逆序数总和,现在就是求4 1 2 3的逆序数总和,大大节省了空间压力(树状数组的长度是数据范围)。
两份代码都是407 ms,甚是诡异。
#include <iostream> #include <stdio.h> #include <cstring> using namespace std; const int MAX_N = 500000 + 1000; int n; int a[MAX_N],t[MAX_N]; //__int64 cnt; long long cnt; void merge_sort(int *A,int x,int y,int *T) //左开右闭 { if(y - x > 1) { int m = x + (y - x) / 2; //划分 int p = x,q = m,i = x; merge_sort(A,x,m,T); //递归求解 merge_sort(A,m,y,T); //递归求解 while(p < m || q < y) { if(q >= y || (p < m && A[p] <= A[q])) //从左半数组复制到临时空间 T[i++] = A[p++]; else //从右半数组复制到临时空间 { T[i++] = A[q++]; cnt += m - p; } } for(i = x;i < y;i++) A[i] = T[i]; } } int main() { while(scanf("%d",&n) != EOF,n) { cnt = 0; memset(a,0,sizeof(a)); memset(t,0,sizeof(t)); for(int i = 0;i < n;i++) scanf("%d",&a[i]); merge_sort(a,0,n,t); //printf("%I64d\n",cnt); printf("%lld\n",cnt); } return 0; }
#include <iostream> #include <stdio.h> #include <algorithm> #include <cstring> using namespace std; struct Node { int val,i; }; const int MAX_N = 500000 + 1000; int n; int arr[MAX_N]; int num[MAX_N]; Node node[MAX_N]; long long cnt; bool cmp(Node a,Node b) { return a.val < b.val; } int lowbit(int i) { return i & (-i); } int sum(int i) { int ans = 0; while(i > 0) { ans += num[i]; i -= lowbit(i); } return ans; } void add(int i,int val) { while(i <= n) { num[i] += val; i += lowbit(i); } } int main() { while(scanf("%d",&n),n) { memset(num,0,sizeof(num)); cnt = 0; for(int i = 1;i <= n;i++) { scanf("%d",&node[i].val); node[i].i = i; } sort(node + 1,node + 1 + n,cmp); arr[node[1].i] = 1; for(int i = 2;i <= n;i++) { if(node[i].val == node[i - 1].val) arr[node[i].i] = i - 1; else arr[node[i].i] = i; } for(int i = 1;i <= n;i++) { add(arr[i],1); cnt += sum(n) - sum(arr[i]); } printf("%lld\n",cnt); } return 0; }