浅谈树状数组求逆序对

做了一道树上求逆序对的题,主要难点并不在于树形结构,而是求逆序对数。(在我看来是这样的)。

to洛谷P3605晋升者计数。

发现自己树状数组求逆序对还有个坑,先填上再说。再加上最近学的树状数组离散化,捋一捋思路。

首先是离散化

for(int i=1;i<=n;i++){
	a[i].v=read();
    a[i].id=i;
}
sort(a+1,a+1+n);按v排序
for(int i=1;i<=n;i++)b[a[i].id]=i;

在上述代码中,首先我们输入的是a[i].v,也就是一开始的数据,我们将其放到结构体里,再记录一下id,也就是原序。之后我们将a按照v排序,那么得到的就是从小到大的顺序。

又因为离散化是为了避免数据太大而出锅,所以我们在乎的就是数据的相对顺序,所以很容易地想到,将他们的顺序作为新的权值,这样就可以保证相对大小不变并且还可以最大化的缩小数据范围。

那么我们开一个新的数组,对应为离散化之后的数组,那么这个数组一定是要和原数组的顺序相同的,所以这个时候原来记录的id也就是原序就有了用场,然后再把当前这个元素的排名也就是i赋给离散化数组的第a[i].id位即可。

再来说逆序对数,在一个序列中,如果存在

\[i<j并且a[i]>a[j] \]

那么就代表有一个逆序对。

考虑如何用树状数组实现\(nlogn\)的时间复杂度求逆序对数。

首先,如果数据大我们需要离散化。

然后从1到n枚举,考虑当前枚举到的位置i,他对总的逆序对的个数的贡献就是他前面的权值大于他的权值的数的个数,所以我们在i的权值的位置修改树状数组,表示i的权值这个数已经出现过了,那么到了后面就可以直接查询从1到i的权值的位置的总和,因为1代表有,0代表没有,所以返回的值即为从一到i小于等于他的权值的数有几个,单步容斥即可得到大于他的数目也就是逆序对数。

code

for(int i=1;i<=n;i++){
	update(b[i],1);
    ans+=query(b[i]);
}
posted @ 2018-10-04 14:00  _王小呆  阅读(3148)  评论(0编辑  收藏  举报