树状数组
简介
用大的节点表示小的节点的信息,在操作的时候可以通过某种方式快速得到小节点的信息。上图中吧原数组a1~a8划分成了若干大块的c数组来维护。如何分块?这里就引入了二进制,能够体会到其精妙。
具体实现
c数组存的是某一段区间的和
如何构建c数组?又是如何c数组快速求出区间的和呢?
lowbit
这个操作可以得到数字二进制下最低位1的位置
例如
lowbit(111)—> 001
lowbit(10100) —> 00100
int lowbit(int x)
{
return x & (-x);
}
构建
数组 c[i] = a[i - lowbit(i) ] + 1 ~ a[i]
举例:
n = (1010101) 2进制
Cx = a[1010101] ~ a[1010101]
Cy = a[1010001] ~ a[1010100]
Cz = a[1000001] ~ a[1010000]
Cq = a[0000001] ~ a[1000000]
从下往上看的话正好区间完全拼接为1~1010101
查询
利用以上二进制的性质进行快速的区间前缀和查询
例如查询1~x,那么就从大到小依次累加C数组的值(这个操作在循环中一直lowbit就可以)
如果查询5~x 那么就query(x) - query(4)即可
上代码:
结合上边的图会更加清楚
int query(int x)
{
int res = 0;
//例如求前六个数的前缀和
//通过x-lowbit(x)我们只需要累加c(4)和c(6)即可
for(;x;x-=lowbit(x)) res += c[x];
return res;
}
修改
结合上边的图我们发现如果修改了c(4),那么c(8)也会改变,所以修改的时候需要+=lowbit(x),把其虚拟的树中的父节点也改变。
void modify(int x, int d)
{
for(;x<=n;x+=lowbit(x)) c[x] += d;
}
算法应用
单点修改区间查询
最简单的树状数组模板就可以完成
#include <bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int N = 2e5+10;
int n, q;
ll a[N];
ll c[N];
int query(int x)
{
ll res = 0;
for(; x; x -= x & (-x)) res+=c[x];
return res;
}
void modify(int x, ll s)
{
for(;x <= n;x += x & (-x)) c[x] += s;
}
signed main()
{
scanf("%lld%lld",&n,&q);
for(int i = 1; i <= n; i ++)
{
scanf("%lld",&a[i]);
modify(i, a[i]);
}
int op, x;
ll d;
while(q--)
{
scanf("%lld%lld",&op, &x);
if(op==1)
{
scanf("%lld", &d);
*斜体样式* modify(x, d - a[x]);
a[x] = d;
}
else
{
printf("%lld\n", query(x));
}
}
}
区间修改区间查询
树状数组做到区间修改需要维护差分数组才能做到。
差分数组的前缀和是原数组。
那么我们可以推导公式:
a1 = d1
a2 = d1 + d2
a3 = d1 + d2 + d3
…
sum1 = a1 = d1
sum2 = a1 + a2 = 2d1 + d2
sum3 = a1 + a2 + a3 = 3d1 +2d2 + d3
…
所以sum(x) = nd1 + (x-1)d2 + (x-2)d3 + … + dn
可以推导出:
如题:
1、求区间和 2、使区间所有数+d
那么这题根据上述公式开两个树状数组就可以解决
// 添加
modify1(l, d);
modify1(r+1,-d);
modify2(l, l*d);
modify2(r+1,(r+1)*(-d));
// 查询
printf("%lld\n", (x+1)*query1(x)-query2(x));
求逆序对
求逆序对是一个静态的问题,我们需要将静态转化为动态。即一边插入一边统计逆序对的个数。每一次把一个新的a[i]放入的时候,通过树状数组查询出已经存在的数中比它大的数的个数,并且可以肯定这些比它大的数一定是在a[i]前面的,一定构成逆序对。所以树状数组是用来统计每个数出现的次数。
移步我的另一篇超详细的文章:
https://blog.csdn.net/qq_41829492/article/details/124073384
树状数组上二分
https://blog.csdn.net/qq_41829492/article/details/123984745
有错误请提出 我会立即纠正