思路:线段树,求最小逆序数,先可以通过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; }
HDU - 1394 - Minimum Inversion Number (线段树 - 单点更新,区间求和)
原文地址:http://blog.csdn.net/u014355480/article/details/45692095