树状数组
首要用途:维护序列的前缀和
对一个序列a,建立一个数组c,其中c[x]保存序列a的区间(x-lowbit(x)+1,x]中所有数的和(前开后必)。
性质:
1.每一个节点x,有c[x]保存着以x为根节点的所有叶节点的和
2.每个内部节点c[x]的子节点个数等于lowbit(x)的位数
3.除了树根以外的每个子节点的父节点都是c[x+lowbit(x)];
4.数的深度为log(N) //N为序列a的长度
tips
- O(logN)的时间复杂度去实现单点修改和区间查询
- 数组下标一定要从1开始
- 一个数的二进制表示中末尾有几个0就在第几层
lowbit
x & (-x)
: 假设 x的二进制数表示 的从右向左数的第一个1所在位为k,则lowbit(x) = 2^(k-1)
tip: 求 n 的二进制表示的第 k 位(从0开始)数字:n >> k & 1
功能
- 实现单点修改
- 实现求前缀和
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
#include <bits/stdc++.h> using namespace std;
const int N = 1e5+10;
int n,m; int t[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>0; i-=lowbit(i)){ sum += tr[i]; } return sum; } int main(){ cin >> n >> m; for(int i=1;i<=n;i++){ cin >> t[i]; } for(int i=1;i<=n;i++){ add(i,t[i]); } while(m--){ int sum=0; int k,x,y; cin >> k >> x >> y; if(k == 0){ cout << query(y) - query(x-1) << endl; } else{ add(x,y); } } return 0; }
|