题目描述
(2020年8月2日所写,现在数据范围已提升,本解析代码只供各位参考学习)
树状数组求逆序对的数量:
给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。
输入样例
6
2 3 4 5 6 1
输出样例
5
算法
树状数组
const int N = 1060000
因为这个树状数组的下标是数的范围,不是题中的n,数的个数的范围。
然后利用树状数组的区间查询,去算有多少个数的位置在这个数之前,并且大于他的。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1060000;
int n,m;
int w[N],tr[N];
int lowbit(int x){
return x & -x;
}
void add(int x,int v){
for(int i=x;i<=N;i+=lowbit(i)) tr[i]+=v;
}
int query(int x){
int sum=0;
for(int i=x;i;i-=lowbit(i)) sum+=tr[i];
return sum;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&w[i]);
LL sum=0;
for(int i=0;i<n;i++){
sum+=(query(N)-query(w[i]));
add(w[i],1);
}
printf("%lld",sum);
return 0;
}
离散化才行的
这个代码现在已经过不去了,数列中每个元素的取值范围增加到了1e9
好的好的!感谢提醒!!!
这里提一下,这个地方好像越界了sum+=(query(N)-query(w[i]));最好写成N - 1
我的树状数组的下标是数的范围,
N
已经定义到1060000辣,就无所谓N - 1
,也谢谢提醒~如果写成N确实有问题,如果你把结果中的
sum
放到全局变量的话就不对了。我因为这个调了好长时间。。。sum+=(query(N)-query(w[i]));
大佬我很不懂这一段代码,而且能不能反向求呢..就是 i = n-1 i–
求解答,麻烦大佬了
额这题用树状数组解决巧妙点在于,这个树状数组的下标是数值的范围,不是题中的n(数的个数的范围)。比如插一个数1000,那就在1000的位置
+1
。那么在求逆序对的时候就是计算下有多少排在我前面的数,数值还比我大的。没事儿,还有不懂的话可以给我私信~我最近没登AcWing哈哈,看到一定会给你回复~
大佬,为什么需要查询最大值的前缀和减去当前值的前缀和呢?
为什么这题数的范围是1e6啊....
通常测试数据中最大数字的大小会是
1e6
,所有当1e6
出现的时候,要有树状数组的值标记上这个树状数组的下标是数的范围,不是题中的n,题目中的n是数的个数的范围。
我看题目没写数的范围,如果大于1e6 是不是就得离散化了呢
是的啊啊啊,但是这题y总用的方法是归并排序,想必数据范围肯定是在能开到的范围内。超出的话确实要考虑离散化了
谢谢owo
没事儿