一个数组的逆序数定义为所有元素对应的逆序数的和。
一个元素对应的逆序数指位置在其前面但值大于其本身的元素的个数。
Inversion(s) = ∑# P(s[i]), s[j] ∈ P(s[i]) iff j < i && s[j] > s[i]。
由组合数学知识,一个数组的逆序数等于使其变成有序列(非降序)所进行得相邻元素交换的最少次数。
本题统计逆序数可以用归并排序。
归并排序求逆序数的正确性也很好说明。
逆序数总和为sum,一个二元组(s[i], s[j]) 对sum贡献1当且仅当i < j 且s[i] > s[j]。
归并排序将原序列s分成两段s1, s2。
二元组要么属于其中一个序列,要么横跨分界点。
假设前者在递归过程中已经统计完毕,对于横跨分界点的二元组。
在归并的时候必然是上s2中的某个元素在与s1中某元素比较时值更小。
这次比较贡献一组对sum有贡献的二元组,接着s2中元素下标加一,继续此过程, 直到归并完成。
实际上归并排序的高效性正是因为其一次比较操作等效了多次插入排序的比较。
http://acm.hdu.edu.cn/showproblem.php?pid=4911

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 typedef __int64 LL; 7 8 const int maxn = 1e5 + 10; 9 int s[maxn], A[maxn]; 10 int n, k; 11 LL sum; 12 13 void merge(int l, int r){ 14 int mid = (l + r) >> 1; 15 int i, j, k = 0; 16 for(i = l, j = mid; i < mid && j < r; ){ 17 if(s[j] < s[i]){ 18 sum += mid - i; 19 A[k++] = s[j]; 20 ++j; 21 } 22 else A[k++] = s[i], ++i; 23 } 24 while(i < mid) A[k++] = s[i], ++i; 25 while(j < r) A[k++] = s[j], ++j; 26 memcpy(s + l, A, sizeof(int) * k); 27 } 28 29 void merge_sort(int l, int r){ 30 if(r - l < 2) return; 31 int mid = (l + r) >> 1; 32 merge_sort(l, mid); 33 merge_sort(mid, r); 34 merge(l, r); 35 } 36 37 int main(){ 38 while(~scanf("%d%d", &n, &k)){ 39 for(int i = 0; i < n; i++) scanf("%d", &s[i]); 40 sum = 0; 41 merge_sort(0, n); 42 printf("%I64d ", max((LL)0, sum - k)); 43 } 44 return 0; 45 }