每发现相邻数字顺序颠倒即将其交换,此时序列的逆序数减一.所以求出给定序列逆序和即可.
求逆序和最简单的方法是冒泡排序,但是O(n2)是很不好的算法.
常用的求逆序和方法有归并排序和树状数组,这里使用归并排序方法.实现见此模板.(假设数组下标从1开始)
void merge(int l, int r){ if(l == r) return; int mid = l + r >> 1; merge(l, mid); merge(mid + 1, r); int i = l, j = mid + 1; for(int k = l; k <= r; k++) if(j > r || i <= mid && s[i] < s[j]) tmp[k] = s[i++]; // 注意如果序列中有重复数字需要改为s[i] <= s[j] else tmp[k] = s[j++], ct += mid - i + 1; for(int k = l; k <= r; k++) s[k] = tmp[k]; }
执行该函数后,数组s将被按升序排列,且ct(假设初始化为0)的值即为原序列的逆序数.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; long long s[500010], b[500010], n, ct; void merge(int l, int r){ if(r <= l) return; int mid = l + r >> 1; merge(l, mid); merge(mid + 1, r); int i = l, j = mid + 1; for(int k = l; k <= r; k++) if(j > r || i <= mid && s[i] < s[j]) b[k] = s[i++]; else b[k] = s[j++], ct += mid - i + 1; for(int i = l; i <= r; i++) s[i] = b[i]; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); while(cin >> n){ if(!n) break; ct = 0; for(int i = 1; i <= n; i++) cin >> s[i]; merge(1, n); cout << ct << endl; } return 0; }