前几天考了个树状数组的题.....不会 别人都A了 十分丢人
于是回来重学一遍 幸亏60min学会了(我好菜啊)
首先树状数组是一种树形结构,与线段树不同的是它只能查前缀和,只支持单点修改。(以后可能还可以扩展吧)
画出一棵树状数组来之后我们可以看到 对于每一个tr点 存的都是一个区间的和 这个区间范围被特殊的位运算规则存在了tr下标里
单点修改的方式是
void add(int x,int k) {//a[i] + k while (x<=n){ tr[x] += k; x += lowbit(x); } }
我们既然对一个点进行了修改,那么在tr数组中包含了这个点的tr也要进行修改
修改的方式就是在原下标上不断地加他当前的lowbit作为新下标,然后对此tr做加法
如果换成位运算的方式,就是在每一位1上加(这个说法太xie了 我不会说= =)(所以这个范围规则真的很鬼)
求1~x前缀和的方式是
int query(int x){// 1 ~x sum int ret = 0; while(x>0){ ret += tr[x]; x -= lowbit(x); } return ret; }
这也是不断地在当前数上减去他的lowbit然后求和(感性理解(这个真能感性理解但给定义很难))
对于普通数组来说,求范围和是O(n)的,而树状数组是O(logn)的
单点修改普通数组O(1),树状数组O(logN)(其中N为数组总大小)
区间修改普通数组加查分O(1)树状数组O(logN)(其中N为数组总大小)
最后给出luogu树状数组2的代码
#include<bits/stdc++.h> using namespace std ; const int MAXN = 500010; int tr[MAXN],n,m,a[MAXN],x,y,z; int lowbit(int x){return x & (-x);} void add(int x,int k) {//a[i] + k while (x<=n){ tr[x] += k; x += lowbit(x); } } int query(int x){//1~x sum int ret = 0; while(x>0){ ret += tr[x]; x -= lowbit(x); } return ret; } signed main(){ ios::sync_with_stdio(false); cin>>n>>m; int last=-1,now=-1; cin>>last; a[1] = last; for(int i=2;i<=n;i++){ cin>>now; a[i] = now - last; last = now ; } for(int i=1;i<=n;i++){ add(i,a[i]); } // for(int i=1;i<=n;i++){ // cout<<"a["<<i<<"]="<<a[i]<<endl; // } for(int i=1;i<=m;i++){ int flag; cin>>flag; if(flag == 1){ cin>>x>>y>>z; add(x,z); add(y+1,-z); } if(flag == 2){ cin>>x; cout<<query(x)<<endl; } } return 0; }
像这种基础数据结构就要常常回来复习,万一哪天考了呢
TAG:SIN_XIII