zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    线程池全面总结
    有状态(Stateful)与无状态(Stateless)
    Callable接口--有返回值的线程
    集合类--最详细的面试宝典--看这篇就够用了(java 1.8)
    [Android App]IFCTT,即:If Copy Then That,一个基于IFTTT的"This"实现
    应朋友死皮白咧地邀请贴一个招聘广告
    [Android]Android焦点流程代码分析
    [Android]Android内存泄漏你所要知道的一切(翻译)
    [Android]Gradle 插件 DiscardFilePlugin(class注入&清空类和方法)
    [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView)
  • 原文地址:https://www.cnblogs.com/fancy-itlife/p/4338589.html
Copyright © 2011-2022 走看看