Minimum Inversion Number 1394(线段树法)
http://acm.hdu.edu.cn/showproblem.php?pid=1394
线段树记录区间[a,b]里面出现了多少个数字,查询时只要查询区间[x,n-1]有多少个数出现过,就知道前面有多少个数比x大,也就知道x有多少逆序数。
把一个序列最前面的数字x移到最后就会产生n-1-x-x个逆序:
x在最前时它后面有x个数(0到x-1)比x小,所以把x移到最后以后逆序数要减x个;
x在最前时它后面有n-1-x个数(x+1到n-1)比x大,所以把x移到最后以后逆序数增加n-1-x个;
所以,最前面的数字x移到最后就会新产生n-2*x-1个逆序
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define lson l,mid,i<<1 5 #define rson mid+1,r,i<<1|1 6 using namespace std; 7 int n; 8 int sum[5010*3]; 9 int query(int ql,int qr=n-1,int l=0,int r=n-1,int i=1) 10 { 11 if(ql<=l&&r<=qr) return sum[i]; 12 int ret=0,mid; 13 mid=(l+r)>>1; 14 if(ql <= mid) ret+=query(ql,qr,lson); 15 if(qr > mid) ret+=query(ql,qr,rson); 16 return ret; 17 } 18 void update(int ql,int l=0,int r=n-1,int i=1) 19 { 20 if(l==r) {sum[i]++;return;} 21 int mid=(l+r)>>1; 22 if(ql<=mid) update(ql,lson); 23 else update(ql,rson); 24 sum[i]=sum[i<<1]+sum[i<<1|1]; 25 } 26 int x[5000]; 27 int main() 28 { 29 while(cin>>n) 30 { 31 int ret=0; 32 memset(sum,0,sizeof(sum));//这里相当于建树 33 for(int i=0;i<n;i++) 34 { 35 scanf("%d",x+i); 36 ret+=query(x[i]); 37 update(x[i]); 38 } 39 int ans=ret; 40 for(int i=0;i<n;i++) 41 { 42 ret+=n-x[i]-x[i]-1; 43 ans=min(ans,ret); 44 } 45 printf("%d\n",ans); 46 } 47 return 0; 48 }