标签:
题意: 输入0 ~ n-1 总共n个数,每次取最前面的一个数放到序列最后面形成新的序列,问这些序列中逆序对数目最少的是多少。
如: 输入0,3,2,4,1,5
先求逆序对数目,第一次将0放到最后 3,2,4,1,5,0 再求逆序对数目,依次下去,取逆序对数目最小值。
用线段树求最初输入的序列的逆序对。
求逆序对有两种思想,一是:求a[i]后面比它小的数的个数,累加。二是:求a[i]前面比它大的数的个数,累加。线段树用的是第二种思想
每次将a[i]插入,就将区间为[i,i]的叶子结点值置1.
如我们求[a[i], n-1]区间内的1的个数,就是求a[i]前面比a[i]大的数的个数,因为比a[i]大的数已经插入并且在[a[i], n-1]内。
每次移动最前面的数到最后面,逆序对数目变化是 - a[i] + (n - 1) - a[i]
因为a[i]后面有a[i]个比它小的数,有(n - 1) - a[i]个比它大的数,所以每次移动,逆序对减少a[i] ,增加(n - 1) - a[i].
#include<iostream> #include<cstdio> #include<cstdlib> #include<ctime> #include<algorithm> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 5050; int num[MAXN << 2]; void PushUp(int rt) { num[rt] = num[rt << 1] + num[rt << 1 | 1]; } void build(int l, int r , int rt) { if(l == r) { num[rt] = 0; //建树叶子结点值置0 return; } int mid = ( l + r) >> 1; build( l, mid, rt << 1); build(mid + 1, r, (rt << 1) +1 ); PushUp(rt); } void update(int p, int l, int r, int rt) { if(l == r) { num[rt] ++; //插入一个数,将其对应的叶子结点值置1 return; } int mid = ( l + r ) >> 1; if(p <= mid) update(p, l, mid, rt << 1); else update(p, mid + 1, r, (rt << 1) + 1); PushUp(rt); } int query(int ll,int rr,int l,int r,int rt) { if(ll <= l && r <= rr) { return num[rt]; } int mid = (l+r) >> 1; int res = 0; if(ll <= mid) res += query(ll,rr,l,mid,rt << 1); if(rr > mid) res += query(ll,rr,mid+1,r,(rt << 1)+1); return res; } int main() { int n; int a[MAXN]; while(~scanf("%d",&n)) { build(0,n-1,1); int res = 0; for(int i = 0; i< n; i++) { scanf("%d",&a[i]); res += query(a[i],n-1,0,n-1,1); //求(a[i],n-1)区间内1的个数 update(a[i],0,n-1,1); } int ans = INF; for(int i = 0; i < n; i++) { res = res -a[i] + n - 1 - a[i]; //没次移动,次序对数目变化 ans = ans < res ? ans : res; } printf("%d\n",ans); } }
hdu 1394 Minimum Inversion Number
标签:
原文地址:http://www.cnblogs.com/yong-hua/p/4657137.html