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 }
原文地址:https://www.cnblogs.com/qijinbiao/p/2619405.html