• 求逆序对[树状数组] jdoj


    求逆序对

      题目大意:给你一个序列,求逆序对个数。

      注释:n<=$10^5$。

        此题显然可以跑暴力。想枚举1到n,再求在i的后缀中有多少比i小的,统计答案即可。这显然是$n^2$的。这...显然过不去,我们思考如何优化?显然,这里的有些过程是重复的。我们将这个序列设为a序列,对于两个1到n中的整数i<j,在j后面的数我们进行了多次重复枚举,我们思考如何优化。容易想到用一个桶来记录。只需要记录对于每一个数来讲,我后面有多少个数是比我小的,只需要将桶中的数累加即可。但是,我们必须记录是这个数之后的桶的含义,也就是说这个数之前的桶我们不可以进行修改,所以我们必须逆向枚举,这时一种做法,但是我们这里讲另一种做法:正向枚举。我们只需要记录这个数之前的桶的状况即可,查询时,只需要记录当前桶的前缀和,但是,我们如何修改这个前缀和?如果用数组实现的话修改是O(n)的,所以这个时间复杂度还是$n^2$的。而这个过程我们可以用树状数组维护。树状数组是针对桶的,也就是说,我树状数组记录的也是区间和,只不过这个区间和是针对桶的。我们在修改的时候只需要对于前面的,所有能看见这个数的树状数组进行修改。每个树状数组的节点的视野就是它后面的数。在查询时,我们只需要查询在这个数视野之内的数。显然,我们对于视野的理解是贪心的,即,这个数只能看见比自己大的数的桶。

      最后,附上丑陋的代码......

     1 #include <iostream>
     2 #include <cstdio>
     3 #define N 100010
     4 using namespace std;
     5 typedef long long ll;
     6 int n,a[N],sum[N];
     7 ll ans;
     8 inline void fix(int x)
     9 {
    10     while(x)sum[x]++,x-=x&(-x);
    11 }
    12 inline int getsum(int x)
    13 {
    14     int re=0;
    15     while(x<=100000)re+=sum[x],x+=x&(-x);
    16     return re;
    17 }
    18 int main()
    19 {
    20     scanf("%d",&n);
    21     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    22     for(int i=1;i<=n;i++)
    23     {
    24         ans+=getsum(a[i]+1);
    25         fix(a[i]);
    26     }
    27     printf("%lld",ans);
    28     return 0;
    29 }

      真难理解啊,博主智商低啊!!

  • 相关阅读:
    C# 反射 通过类名创建类实例
    c#委托把方法当成参数
    PPT美化大师
    以Outlook样式分组和排列数据项
    使用windows服务和MSMQ和进行日志管理(解决高并发问题)
    springboot配置filter
    filter 中用spring StopWatch 监控请求执行时间
    spring计时工具类stopwatch用法
    Spring异步任务处理,@Async的配置和使用
    注解用法详解——@SuppressWarnings
  • 原文地址:https://www.cnblogs.com/ShuraK/p/8039382.html
Copyright © 2020-2023  润新知