分析:线段树的一个应用,求逆序数。
从a[0]开始扫描,找比a[0]大的个数,也就是询问区间(a[0],n-1)中点的个数,然后再插入a[0],更新父节点即可。
思路为:tree[i]为0代表i未出现,为1则已经出现,
然后查询时我们只要统计新加入的数到n-1这个范围内有多少个tree[i]为1,也就是当前
数的逆序,然后累加,就是我们要求的逆序数。因为其他形式都可以由第一次求得的逆
序数递推而来,所以只需计算一次。
有个结论,设逆序数为sum,x[0]后面比它小的一定是x[0]个。那么移到末尾后,比x[0]大的数的后面比它小的数统统加一,也就是加(n - a[0] - 1),然后它放到末尾了,他原来的后面比它小的数变为0,也就是sum = sum + (n - a[0] - 1) - a[0];
AC代码:
1 #include<stdio.h> 2 #define maxn 5010 3 #define lson l,m,rt << 1 4 #define rson m+1,r,rt << 1 | 1 5 int tree[maxn<<2],x[maxn]; 6 int min(int a,int b) 7 { 8 return a<b?a:b; 9 } 10 void PushUP(int rt) 11 { 12 tree[rt]=tree[rt<<1]+tree[rt<<1|1]; 13 } 14 void build(int l,int r,int rt) 15 { 16 tree[rt]=0; 17 if(l==r) 18 return ; 19 int m=(l+r)>>1; 20 build(lson); 21 build(rson); 22 } 23 int query(int L,int R,int l,int r,int rt) 24 { 25 if(L<=l&&R>=r) 26 return tree[rt]; 27 int m=(l+r)>>1; 28 int ret=0; 29 if(L<=m) 30 ret+=query(L,R,lson); 31 if(R>m) 32 ret+=query(L,R,rson); 33 return ret; 34 } 35 void update(int p,int l,int r,int rt) 36 { 37 if(l==r) 38 { 39 tree[rt]++; 40 return ; 41 } 42 int m=(l+r)>>1; 43 if(p<=m) 44 update(p,lson); 45 else 46 update(p,rson); 47 PushUP(rt); 48 } 49 int main() 50 { 51 int i,n,sum; 52 while(scanf("%d",&n)!=EOF) 53 { 54 build(0,n-1,1); 55 sum=0; 56 for(i=0;i<n;i++) 57 { 58 scanf("%d",&x[i]); 59 sum+=query(x[i],n-1,0,n-1,1); 60 update(x[i],0,n-1,1); 61 } 62 int ret=sum; 63 for(i=0;i<n;i++) 64 { 65 sum+=n-x[i]-1-x[i]; 66 ret=min(ret,sum); 67 } 68 printf("%d\n",ret); 69 } 70 return 0; 71 }