博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[模板]树状数组
阅读量:5359 次
发布时间:2019-06-15

本文共 4373 字,大约阅读时间需要 14 分钟。

先膜 orz

基础

设lowbit(x)表示的是把x的二进制只留下最低一位的1,然后lowbit(x)=x&(-x) (我也不知道为什么)

设c[x]表示从i往前一共lowbit(x)个数的和,那么x-lowbit(x)就是c[x]表示的范围的前一个数。

然后可以得到c[x]=c[x-lowbit(x)+lowbit(x)>>1]+c[x-lowbit(x)+lowbit(x)>>1+lowbit(x)>>2]+c[x-lowbit(x)+lowbit(x)>>1+lowbit(x)>>2+lowbit(x)>>3]....+c[x-1]+a[x]。

也就是说,如果将这个关系做成一个树,x的父亲就是x+lowbit(x)(观察一下上面的式子,很容易发现每一项都满足)

要询问1到x的和的话,只要一直加c[x],然后让x-=lowbit(x)直到x=0就好了

然后要修改一个点x的话,就一直往上修改它的父亲直到x>n就可以了。

也就是说,最基本的树状数组支持的是单点修改和前缀和查询,然后前缀和可以很容易地做成区间和(端点减一减)。

luogu3374

1 #include
2 #include
3 #include
4 #define lowbit(x) ((x)&(-(x))) 5 #define LL long long int 6 const int maxn=500050; 7 8 inline int rd(){ 9 int x=0;char c=getchar();int neg=1;10 while(c<'0'||c>'9'){
if(c=='-') neg=-1;c=getchar();}11 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();12 return x*neg;13 }14 15 int N,M;16 LL c[maxn];17 18 inline void add(int x,int y){19 while(x&&x<=N) c[x]+=y,x+=lowbit(x);20 }21 inline LL query(int x){22 LL re=0;while(x) re+=c[x],x-=lowbit(x);return re;23 }24 25 int main(){26 int i,j,k;27 N=rd(),M=rd();28 for(i=1;i<=N;i++) add(i,rd());29 for(i=1;i<=M;i++){30 int a=rd(),b=rd(),c=rd();31 if(a==1) add(b,c);32 else printf("%lld\n",query(c)-query(b-1));33 }34 }
View Code

 

也可以支持区间修改和单点查询,只要做一个差分,区间修改就变成了两个端点的修改,单点查询就变成了前缀和查询。

luogu3368

1 #include
2 #include
3 #include
4 #define lowbit(x) ((x)&(-(x))) 5 #define LL long long int 6 const int maxn=500050; 7 8 inline int rd(){ 9 int x=0;char c=getchar();int neg=1;10 while(c<'0'||c>'9'){
if(c=='-') neg=-1;c=getchar();}11 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();12 return x*neg;13 }14 15 int N,M;16 LL c[maxn];17 18 inline void add(int x,int y){19 while(x&&x<=N) c[x]+=y,x+=lowbit(x);20 }21 inline LL query(int x){22 LL re=0;while(x) re+=c[x],x-=lowbit(x);return re;23 }24 25 int main(){26 int i,j,k;27 N=rd(),M=rd();28 for(i=1,j=0;i<=N;i++){29 k=rd();add(i,k-j);j=k;30 };31 for(i=1;i<=M;i++){32 int a=rd(),b=rd();33 if(a==1){34 int c=rd(),d=rd();35 add(b,d);add(c+1,-d);36 }37 else printf("%lld\n",query(b));38 }39 }
View Code

 

升级

区间修改和区间查询也是可以支持的,然后我选择线段树......

树状数组写起来真方便,suki

$$前n项和\sum_{i=1}^{n}{a[i]}=\sum_{i=1}^{n}{\sum_{j=1}^{i}{d[j]}}(差分数组)=\sum_{i=1}^{n}{(n-i+1)d[i]}=(n+1)\sum_{i=1}^{n}{d[i]}-\sum_{i=1}^{n}{i*d[i]}$$

这样的话,只要同时维护d[i]和i*d[i],就可以做到区间修改和区间查询了。

luogu3372

1 #include
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #define pa pair
11 #define lowb(x) ((x)&(-(x)))12 #define REP(i,n0,n) for(i=n0;i<=n;i++)13 #define PER(i,n0,n) for(i=n;i>=n0;i--)14 #define MAX(a,b) ((a>b)?a:b)15 #define MIN(a,b) ((a
'9'){ if(c=='-') neg=-1;c=getchar();}25 while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();26 return x*neg;27 }28 29 int N,M;30 ll tr[maxn],itr[maxn];31 32 inline void add(int x,ll y){33 ll yy=y*x;34 while(x<=N){35 tr[x]+=y;itr[x]+=yy;x+=lowb(x);36 }37 }38 inline ll query(int x){39 ll re=0,n=x;40 while(x){41 re+=(n+1)*tr[x]-itr[x];x-=lowb(x);42 }return re;43 }44 45 int main(){46 rei i,j,k;47 N=rd(),M=rd();ll lst=0;48 for(i=1;i<=N;i++){49 ll x=rd();add(i,x-lst);lst=x;50 }for(i=1;i<=M;i++){51 int a=rd(),b=rd(),c=rd();52 if(a==1){53 ll k=rd();add(b,k);add(c+1,-k);54 }else{55 printf("%lld\n",query(c)-query(b-1));56 }57 }58 return 0;59 }
View Code

 

二维的话也是可以做的,就相当于每跳到一个x都跳一遍y的lowbit,而且可以支持区间修改和区间查询

单点修改区间查询不多说,来说说区间修改和查询

仿照一维的形式,我们先得到差分数组

因为差分数组的前缀和就是原来那个点的值,所以能得到$d[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]$

所以如果是(x1,y1,x2,y2)区间加的话,就是给(x1,y1),(x2+1,y2+1)加,给(x1,y2+1),(x2+1,y1)减(手画一下)

然后是查询,也是有$(x1,y1,x2,y2)=S[x2][y2]-S[x1-1][y2]-S[x2][y1-1]+S[x1-1][y1-1]$

然后有

$$S[x][y]=\sum\limits_{i=1}^{x}{\sum\limits_{j=1}^{y}{\sum\limits_{k=1}^{i}{\sum\limits_{l=1}^{j}{d[k][l]}}}}$$

$$=\sum\limits_{i=1}^{x}{\sum\limits_{k=1}^{i}{\sum\limits_{j=1}^{y}{((y+1)d[k][j]-j*d[k][j])}}}$$

$$=\sum\limits_{i=1}^{x}{\sum\limits_{j=1}^{y}{(x+1)(y+1)d[i][j]-(x+1)*j*d[i][j]-(y+1)*i*d[i][j]+i*j*d[i][j]}}$$

于是二维树状数组维护d,id,jd和ijd就行了

 

高级用法以后慢慢填

转载于:https://www.cnblogs.com/Ressed/p/9533461.html

你可能感兴趣的文章
2015-阿里C++研发附加题第一题
查看>>
Scrum学习总结
查看>>
把C#对象转换为json字符串
查看>>
【BZOJ3232】圈地游戏 分数规划+最小割
查看>>
【BZOJ4245】[ONTAK2015]OR-XOR 贪心
查看>>
【BZOJ3678】wangxz与OJ Splay
查看>>
有哪些通俗易懂的例子可以解释 IaaS、PaaS、SaaS 的区别?
查看>>
u盘打开提示要格式化,但是又无法格式化
查看>>
C++之位操作符
查看>>
Thinking in Java Reading Note(7.复用类)
查看>>
poj1611(并查集)
查看>>
使用phpqrcode生成二维码
查看>>
CF123E Maze(期望dp,树形dp,式子)
查看>>
四则运算
查看>>
Java入门:用户登录与注册模块1(实践项目)——分析
查看>>
python数据类型
查看>>
基于梯度的权重更新优化迭代算法
查看>>
单位阶跃函数(Heaviside/unit step function)—— 化简分段函数
查看>>
制作 Gif 工具
查看>>
windows 下 TensorFlow(GPU 版)的安装
查看>>