HDU 1394:Minimum Inversion Number(树状数组,线段树)[水]
题意:有0~n-1这n个数,以一定的排列。这个排列可以循环,就是可以把第一个拿到最后,然后形成新的排列。问这些排列中的逆序对最小值。
思路:
最后的循环,拿走一个之后,新的逆序对数
newsum = oldsum - nowni + ((n-1)-nowni)
其中 ,nowni表示这个数所构成的逆序对数。
Nowni = 原数列中此数往后构成的逆对数 + 原数列中此数往前构成的非逆对数。
然后那两个辅助数组,就可以用扫描循环,然后求前面(或后面)比现在这个数大的(或小的)的数有多少。典型的树状数组。
为了练习线段树,所以写了。
//18:34 //18:53 //19:02 //19:08 过 #include <cstdio> #include <cstring> #define lson l, mid, rt<<1 #define rson mid+1, r, rt<<1|1 #define N 5020 int tree[N<<4]; int preSun[N]; int ni[N]; int a[N]; void build(int l, int r, int rt) { memset(tree, 0, sizeof(tree)); } void pushUp(int rt){ tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } void update(int p, int v, int l, int r, int rt) { if (l == r) { tree[rt] += v; return; } int mid = (l+r)/2; if (p <= mid) update(p,v,lson); if (p > mid) update(p,v,rson); pushUp(rt); } int query(int L, int R, int l, int r, int rt) { if (L <= l && r <= R) { return tree[rt]; } int ans = 0; int mid = (l+r)/2; if (L <= mid) ans += query(L,R,lson); if (R > mid) ans += query(L,R,rson); return ans; } int main() { int n; while (scanf("%d", &n) != EOF) { for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); a[i]++; } build(1,n,1); for (int i = 1; i <= n; i++) { preSun[i] = query(1,a[i],1,n,1); update(a[i],1,1,n,1); } int nowsum = 0; build(1,n,1); for (int i = n; i >= 1; i--) { ni[i] = query(1,a[i],1,n,1); nowsum += ni[i]; update(a[i],1,1,n,1); } //printf("nowsum = %d\n", nowsum); int mins = nowsum; for (int i = 1; i <= n; i++) { int nows = ni[i] + preSun[i]; nowsum = nowsum - nows + (n-1) - nows; if (nowsum < mins) mins = nowsum; //printf("nowsum = %d\n", nowsum); } printf("%d\n", mins); } return 0; }
posted on 2014-03-17 19:35 ShineCheng 阅读(160) 评论(0) 编辑 收藏 举报