树状数组
应用情况
前缀和能够做到求解区间和,但是如果某一个元素发生了修改那么此时需要重新求解前缀和,才能去求解区间和,那么利用树状数组可以能够在logn的时间复杂度内完成:区间求和,单点修改这两个操作。
树状数组的构造:
看图:
观察这些下标的二进制
1(001) c[1] = a[1]
2(010) c[2] = a[1] + a[2]
3(011) c[3] = a[3]
4(100) c[4] = a[1]+a[2]+a[3]+a[4]
5(101) c[5] = a[5]
6(110) c[6] = a[5] + a[6]
7(111) c[7] = a[7]
8(1000) c[8] = a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
由此可以得到规律: c[i]等于从第i个元素开始往后加
2
k
2^k
2k个元素(
2
k
2^k
2k代表的是最低位1的权值)
其中
2
k
2^k
2k能够利用 lowbit() 得到
代码:
int lowbit(int x){
return x&-x;
}
单点修改:
由这个图我们知道,如果我要修改a[3]的值,那么同时我需要修改的是c[3],c[4],c[8]他们的进位是对应的lowbit()。
单点修改代码:
void updata(int x,int y){
for(int i=x; i<=n; i+=lowbit(i)){
cnt[i] += y;
}
}
区间求和
由此我们观察到,如果求前七个元素之和的化,从这个元素不断的减lowbit()即可
代码:
ll getsum(int x){
ll sum = 0;
for(int i=x; i; i-=lowbit(i)){
sum += cnt[i];
}
return sum;
}
树状数组的应用
利用树状数组能够求解逆序对,这也是较为普遍的应用,他的本质是利用树状数组存储了元素出现的次数。从而达到求解逆序对的方式,但是如果数据会比较大的话需要利用离散化(后续我专门写一篇文章来介绍以下离散化)以下数据。
关键代码:
ll ans = 0;
for(int i=1; i<=n; i++){
int x = mp[b[i]];//离散化之后的数据
updata(x,1);
ans += i - getsum(x);
}
cout<<ans;
今天就到这了拜拜六!!!