HDU 1394 树状数组+离散化求逆序数

对于求逆序数问题,学会去利用树状数组进行转换求解方式,是很必要的。

一般来说我们求解逆序数,是在给定一串序列里,用循环的方式找到每一个数之前有多少个比它大的数,算法的时间复杂度为o(n2)。

那么我们通过树状数组可以明显提高时间效率。

我们可以按照排列的顺序依次将数字放入树状数组中,并依次更新预与之相关联的树状数组元素。那么在将其更新完毕后,我们知道每个数对应的树状数组元素的左边的数肯定比它小,我们在以序列顺序依次更新树状数组时,如果有值在它前面出现,那么它对应的树状数组元素(在这个题目里存放的是个数)值必然增加,我们可以利用树状数组快速求一段区间的总和(这一段是比它小的数字的总个数)。那么用i-sum得到的就是逆序数了。

当然,另外还要注意到一点由于数字可能很大,那么我们开那么大的数组是不合理地,为此我们用离散化的方式来处理r[num[i].tag]=i;r[MAXN]存放离散数据,即用i=1;i<=t;i++从小到大表示递增的数据,以便充分使用每一个数组元素,类似于map的功能,映射。)

所以因为有这一步,所以要先sort,然后将数值离散化。这样每一个数的更新仅需o(log(n))

总的时间降为o(nlog(n))。

带着代码再加深理解:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define MAXN 5005
 6 using namespace std;
 7 struct node
 8 {
 9     int val;
10     int tag;
11 }num[MAXN];
12 int t;
13 int r[MAXN],c[MAXN];//c[MAXN]为已经离散化的树状数组元素
14 bool cmp(node a,node b)
15 {
16     return a.val<b.val;
17 }
18 int lowbit(int x)
19 {
20     return x&(-x);
21 }
22 void update(int x)
23 {
24     while(x<=t)
25     {
26         c[x]+=1;
27         x+=lowbit(x);
28     }
29 }
30 int s(int x)
31 {
32     int sum=0;
33     while(x>0)
34     {
35         sum+=c[x];
36         x-=lowbit(x);
37     }
38     return sum;
39 }
40 int main()
41 {
42     int i,j,ans,min;
43     while(cin>>t)
44     {
45         ans=0;
46         memset(c,0,sizeof(c));
47         for(i=1;i<=t;i++)
48         {
49             scanf("%d",&num[i].val);
50             num[i].tag=i;
51         }
52         sort(num+1,num+t+1,cmp);
53         for(i=1;i<=t;i++)
54         {
55             r[num[i].tag]=i;//进行离散化
56         }
57         for(i=1;i<=t;i++)
58         {
59             update(r[i]);
60             ans=ans+i-s(r[i]);
61         }
62         min=ans;
63         for(i=1;i<t;i++)
64         {
65             ans=ans-r[i]+1+t-r[i];
66             min=ans<min?ans:min;
67         }
68         printf("%d
",min);
69     }
70     return 0;
71 }
原文地址:https://www.cnblogs.com/fancy-itlife/p/4338589.html