树状数组求逆序对 笔记与思路整理

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

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

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

原数组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 
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
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\n",ans);
}

你可能感兴趣的:(树状数组求逆序对 笔记与思路整理)