逆序对之树状数组

[树状数组]逆序对

  • 题目
    • 题目解析
    • 算法简介
      • 树状数组
    • 代码实现

题目

题目解析

  • 逆序对可以用归并排序和树状数组:
  • 归并排序就不用讲了,我们来讲讲树状数组

算法简介

树状数组

  • 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为 log ⁡ 2 n \log_2{n} log2n的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在 log ⁡ 2 n \log_2{n} log2n的复杂度下进行范围修改。
  • 这种数据结构(算法)并没有C++和Java的库支持,需要自己手动实现。在Competitive Programming的竞赛中被广泛的使用。树状数组和线段树很像。
  • 如下图,就是一种树状数组:
    逆序对之树状数组_第1张图片
  • 假设数组 a 1 a 2 … a n a_1 a_2 \ldots a_n a1a2an,那么查询 a 1 a_1 a1 + + + $ \ldots$ + + + a n a_n an的时间是 log ⁡ 2 n \log_2{n} log2n级别的,而且是一个在线的数据结构,支持随时修改某个元素的值,复杂度也为 log ⁡ 2 n \log_2{n} log2n级别。
  • 来观察这个图:
  • 令这棵树的结点编号为 C 1 C_1 C1, C 2 C_2 C2 … \ldots C n C_n Cn。令每个结点的值为这棵树的值的总和,那么容易发现:

C 1 = A 1 C_1 = A_1 C1=A1
C 2 = A 1 + A 2 C_2 = A_1 + A_2 C2=A1+A2
C 3 = A 3 C_3 = A_3 C3=A3
C 4 = A 1 + A 2 + A 3 + A 4 C_4 = A_1 + A_2 + A_3 +A_4 C4=A1+A2+A3+A4
… \ldots
C 16 = A 1 + A 2 + A 3 + A 4 + A 5 + A 6 + A 7 + A 8 + A 9 + A 10 + A 11 + A 12 + A 13 + A 14 + A 15 + A 16 C_{16} = A_1 + A_2 + A_3 + A_4 + A_5 + A_6 + A_7 + A_8 + A_9 + A_{10} + A_{11} + A_{12} + A_{13} + A_{14} + A_{15} + A_{16} C16=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10+A11+A12+A13+A14+A15+A16

  • 这里有一个有趣的性质
  • 设节点编号为 x x x,那么这个节点管辖的区间为 2 k 2^k 2k(其中 k k k x x x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为 A x A_x Ax
  • 所以很明显:

C n C_n Cn = = = A ( A_( A(n$ – – 2 k 2^k 2k + + + 1 1 1)$ + + + … \ldots + + + A n A_n An

  • 算这个 2 k 2^k 2k有一个快捷的办法,定义一个函数如下即可:
int lowbit(int x){
return x&-x;
}

代码实现

#include
#include 
using namespace std;
int n,m,a[500039],x,y,z,s[500039],fs[500039];
long long ans,f[500039],tot;
inline void read(int &x) {
	x=0;char s=getchar();int fs=1;
	while(s<'0'||s>'9') {
    if(s=='-')fs=-1;
    s=getchar();
    }
	while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
	x*=fs;
}
inline void get(int x,int z){
	while(x<=n)f[x]+=z,x+=x&-x;
    }
inline long long find(int x){
	tot=0;
	while(x)tot+=f[x],x-=x&-x;
	return tot;
}
inline bool cmp(int x,int y){return a[x]<a[y];}
int main(){
	system("title \"【树状数组】逆序对\"");
	register int i;
	read(n);
	for(i=1;i<=n;i++)read(a[i]),s[i]=i;
	sort(s+1,s+n+1,cmp);
	fs[s[1]]=1;
	for(i=2;i<=n;i++){
		if(a[s[i]]==a[s[i-1]]) fs[s[i]]=fs[s[i-1]];
		else fs[s[i]]=i;
	}
	for(i=1;i<=n;i++){
		ans+=find(n)-find(fs[i]);
		get(fs[i],1);
	}
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(题解)