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 }

 

你可能感兴趣的:(version)