zoukankan      html  css  js  c++  java
  • 树状数组求逆序对 笔记与思路整理

    刚学会树状数组,正好还有个科技是树状数组可以用的:用树状数组求逆序对,码量要比归并排序小。

    这里只用到单点更新、区间查询的基础树状数组,没有看后面的同学也可以先学一下这个。

    这里直接上一个例子演示一下:

    原数组a[]: 5  3  4  2  1

    设一个数组t[],按原数组顺序,从前往后将 t[a[i]] ++,并将 ans += (i - quary(a[i]) - 1) 。(quary(x)为x的前缀和)

    t[] 的变化过程:

    _  _  _  _  1(a[0] == 5, t[5] ++)ans += 0;

    _  _  1  _  1(a[0] == 5, t[5] ++)ans += 1;

    _  _  1  1  1(a[0] == 5, t[5] ++)ans += 1;

    _  1  1  1  1(a[0] == 5, t[5] ++)ans += 3;

    1  1  1  1  1(a[0] == 5, t[5] ++)ans += 4;

    所以逆序对数 = ans = 9。

    原理也很好理解,逆序对数就等于每个数前面比它大的数个数之和

    但是,用这种方法求逆序对需要开到数组元素最大值的树状数组(类似于桶排序),较大较稀疏数据建议离散化。

    代码实现:

    #include <bits/stdc++.h>
    using namespace std;
    int a[100010], t[100010];
    int n, ans = 0, num;
    int lowbit(int x){
        return x & (-x);
    }
    void add_node(int pos, int val){ //节点pos增加val(这里只用到+1)
        for(int i=pos; i<=n; i+=lowbit(i)){
            t[i] += val;
        }
    }
    int quary_node(int pos){ //求节点pos前缀和
        int ans = 0;
        for(int i=pos; i>0; i-=lowbit(i)){
            ans += t[i];
        }
        return ans;
    }
    int main(){
        cin >> n;
        for(int i=1; i<=n; i++){
            scanf("%d", &num);
            add_node(num, 1);
            ans += (i - quary_node(num));
        }
        cout << ans;
        return 0;
    }

    再分享一个压行非常优秀的代码,来自 da32s1da的洛谷AT2829题解

    #include<cstdio>
    int n,m,ans;
    int a[100005];
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&m);
            for(int j=m;j;j-=(j&(-j)))ans-=a[j];ans+=i-1;
            for(int j=m;j<=n;j+=(j&(-j)))a[j]++;
        }
        printf("%d
    ",ans);
    }
  • 相关阅读:
    私有构造函数(C# 编程指南)
    unshift(), push(),shift(),pop()函数的运用
    flex转载
    二叉树各节点的实现
    关于删除树中指定节点的实例分析
    树的各种操作代码实现
    关于二叉查找树的++迭代器的实现
    利用map,以一个单词为键,以与它相差一个字母的单词组集作为值的算法编程
    逆向单项链表的算法
    给Vector类添加insert
  • 原文地址:https://www.cnblogs.com/miserweyte/p/11569581.html
Copyright © 2011-2022 走看看