http://acm.hdu.edu.cn/showproblem.php?pid=1394
给出一列数组,数组里的数都是从0到n-1的,在依次把第一个数放到最后一位的过程中求最小的逆序数
线段树的应用,先建树,输入一个数,查询在在树中比他大的数的个数,然后把这个数更新进树里,再输入数重复操作,类似于进栈一样,先更新进树的数下标肯定是小于后更新的
这样只求到了一个数组的逆序数,还要有依次把第一个数放到最后的得到新数组的比较,这里有一个结论;如果是0到n的排列,那么如果把第一个数放到最后,对于这个数列,逆序数是减少a[i],又增加n-1-a[i]的,就是加上n-1-2*a[i]的,再进行比较取最小的就行
code
1 #include<cstdio> 2 using namespace std; 3 struct point { 4 int l,r,sum; 5 }; 6 point tree[5001*4]; 7 int a[5001]; 8 int n; 9 void build(int i,int left,int right) 10 { 11 tree[i].l=left,tree[i].r=right; 12 tree[i].sum=0; 13 if (left==right) return ; 14 int mid=(left+right)/2; 15 build(i*2,left,mid); 16 build(i*2+1,mid+1,right); 17 } 18 int find(int i,int pos) 19 { 20 if (pos<=tree[i].l) return tree[i].sum; 21 int mid=(tree[i].l+tree[i].r)/2; 22 int a=0,b=0; 23 if (pos<=mid) 24 a=find(i*2,pos); 25 if (n-1>mid) 26 b=find(i*2+1,pos); 27 return a+b; 28 } 29 void update(int i,int pos) 30 { 31 if (pos==tree[i].l&&pos==tree[i].r) 32 { 33 tree[i].sum=1; 34 return ; 35 } 36 int mid=(tree[i].l+tree[i].r)/2; 37 if (pos<=mid) 38 update(i*2,pos); 39 else 40 update(i*2+1,pos); 41 tree[i].sum=tree[i*2].sum+tree[i*2+1].sum; 42 } 43 int main() 44 { 45 int ans,i,min; 46 while (~scanf("%d",&n)) 47 { 48 if (n==0) break; 49 ans=0; 50 build(1,0,n-1); 51 for (i=1;i<=n;i++) 52 { 53 scanf("%d",&a[i]); 54 ans+=find(1,a[i]+1); 55 update(1,a[i]); 56 } 57 min=ans; 58 for (i=1;i<=n;i++) 59 { 60 ans=ans+n-1-2*a[i]; 61 if (ans<min) 62 min=ans; 63 } 64 printf("%d\n",min); 65 } 66 return 0; 67 }