hdu 1394 线段树计算逆序数
线段树计算逆序数的原理:
用线段树来统计已插入的数的个数(所以要保证最大的那个数不能太大,否则数组都开不了),然后每插入一个数,就查询比插入的数大的个数,累加即可。
这个题还有一个特点就是,题目给的是0至n-1的全排列,也就是说每个数都不同。那么abcde的逆序数与bcdea的逆序数就很明了了。
假设比a小的数有t个,那么比a大的数有n-t-1个,那么abcde转换至bcdea的逆序数就增加了n-t-1,减少了t
不需要build函数建树,因为初始状态没有数插入,直接menset就可以了
#include <bits/stdc++.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 using namespace std; const int MAXN = 5008; int cnt[MAXN<<2]; int query(int L, int R, int l, int r, int rt) { if(L <= l && r <= R) return cnt[rt]; int ret = 0; int m = (l + r) >> 1; if(L <= m) ret += query(L, R, lson); if(R > m) ret += query(L, R, rson); return ret; } void updata(int p, int l, int r, int rt) { if(l == r) { cnt[rt]++; return; } int m = (l + r) >> 1; if(p <= m) updata(p, lson); else updata(p, rson); cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1]; } int a[MAXN]; int main() { // freopen("in.txt", "r", stdin); int n; while(~scanf("%d", &n)) { memset(cnt, 0, sizeof(cnt)); int sum = 0; for(int i=0; i<n; i++) { scanf("%d", &a[i]); sum += query(a[i]+1, n-1, 1, n, 1); updata(a[i], 1, n, 1); } int ans = sum; for(int i=0; i<n; i++) { sum += n - 2*a[i] - 1; ans = min(ans, sum); } printf("%d\n", ans); } return 0; }