思路:线段树,求最小逆序数,先可以通过n*logn的时间用线段树求出初始的逆序对数,然后递推求出其他的解,递推过程看代码
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <deque>
#include <cctype>
#define LL long long
#define INF 0x7fffffff
using namespace std;
const int maxn = 50005;
int n;
int sum[maxn << 2];
int a[maxn];
void build(int l, int r, int rt) { //初始建树
sum[rt] = 0;
if(l == r) return;
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
}
int query(int L, int R, int l, int r, int rt) { //区间查询
if(L <= l && r <= R) {
return sum[rt];
}
int mid = (l + r) >> 1;
int ret = 0;
if(mid >= L) ret += query(L, R, l, mid, rt << 1);
if(R >= mid + 1) ret += query(L, R, mid + 1, r, rt << 1 | 1);
return ret;
}
void update(int p, int l, int r, int rt) { //更新
if(l == r) {
sum[rt] ++;
return;
}
int mid = (l + r) >> 1;
if(p <= mid) update(p, l, mid, rt << 1);
else update(p, mid + 1, r, rt << 1 | 1);
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int main() {
while(scanf("%d", &n) != EOF) {
build(0, n - 1, 1);
int tt = 0;
for(int i = 0; i < n; i ++) {
scanf("%d", &a[i]);
tt += query(a[i], n - 1, 0, n - 1, 1);//看之前插入的有多少个比当前值大
update(a[i], 0, n - 1, 1);//插入当前值
}
int ans = tt;
for(int i = 0; i < n; i ++) {//递推求最小逆序对数
tt += (n - 1 - a[i]) - a[i];//每次移动最前面那个数,就增加(n-1-a[i])个逆序对,减少(a[i])个逆序对
ans = min(ans, tt);
}
cout << ans << endl;
}
return 0;
}