图片来自度娘~~

树状数组形如上图,是一种快速查找区间和,快速修改的一种数据结构,一个查询和修改复杂度都为log(n),树状数组1和树状数组2都是板子题,在这里进行详解;

求和:

首先我们看一看这个图’

A数组对应各个元素的值,c数组用来求和和修改。

有连线代表着此节点的值为连线下全部子节点的和such as   c[4]=c[2]+c[3]+A[4]=A[1]+A[2]+A[3]+A[4];

貌似没有什么神仙规律。。。。。。小学找规律题都不会了嘤嘤嘤

那么我们看一下:

C1 = A1                   对应的:1=2^0
C2 = A1 + A2                         2=2^1
C3 = A3                       3=2^1+2^0
C4 = A1 + A2 + A3 + A4                4=2^2
 
C5 = A5                       5=2^2+2^0
C6 = A5 + A6                    6=2^2+2^1
C7 = A7                       7=2^2+2^1+2^0
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8       8=2^3
那么我们按照右边的拆分,如果要询问前7个元素(前7个A的和)的和,那么我们可以把7分成如上的3个分段(区间),分别预处理这3个区间的和,把时间复杂度降到log级别的而不是分别查找7个元素并累加7次。
那么我们找到了降低时间复杂度的方法了,但是怎么实现?
换句话说怎么把一个数拆成这种区间呢?
这里我们用一个神奇的东西叫做lowbit(x), 用来一位一位的把x拆分成以上这种形式。
我们发现,以上的形式就是一个数的二进制分解!
那么我们在将一个任意自然数表示成二进制的时候,只要每次获取这个数二进制表示的最后为值为1的一位,并每次减去它,直到这个数为0为止才算拆分完
看着很懵?QWQ
我们还是要拿7举个栗子:
 
 7=2^2+2^1+2^0,也就是7用二进制表示为111;
那么我们获取当前数二进制表示的最后为值为1的一位以及它后面所有的0构成的数,也就是最后的1,表示长度为1的区间,获取完毕后,我们减去c[7];现在数变为110,和ans加上c[7]
我们获取当前数二进制表示的最后为值为1的一位以及它后面所有的0构成的数,也就是第二位的1,表示长度为2的区间,获取完毕后,我们减去c[7-1]也就是c[6];现在数变为100,和ans加上c[6]
我们获取当前数二进制表示的最后为值为1的一位以及它后面所有的0构成的数,也就是开头的1,表示长度为4的区间,获取完毕后,我们减去c[6-2]也就是c[4];现在数为0,和ans加上c[4]
那么,我们成功把前7个数的和分解为c[7],c[7-2^0]和c[7-2^0-2^1]三个区间,对照上图,我们发现ans成功表示了前7个数(A)的和。你看一下就知道了嘛。。。QWQ
 
lowbit(x)公式就是x&(-x),
这是啥
1.我们对原数先取反,(就是在二进制表示下0变1,1变0,7(111)取反为000)
2.然后加一(000+1=001)
3.然后进行&运算(对于当前二进制数位,如果都相同(同为1或0),就返回1,else就为0)(111&001=1)
那么lowbit(7)就位1,即从右往左数数到第一个非零位的数和它后面所有的0构成的数。
概念算是讲清了,那么公式也讲一下:
对于第一步:x=~x
第二步:~x+1也就是-x,具体为什么要看电脑存储原理二进制补码,来源度娘。
第三步,与运算:x&(~x+1)也就是x&-x
至此,求和方法讲解完毕;
求和函数代码:
int query(int x){
int ans=;
while(x!=){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
修改:
对于修改操作,只要查把后面元素和当前项有关的都加上修改的值就OK了,换句话说就是只要当前项能够影响到的后面的项,就都修改。
也就是把-lowbit(x)换成+lowbit(x)其余没大区别
代码:
void update(int x,int k){
while(x<=n){//上界
tree[x]+=k;
x+=lowbit(x);
}
}

然后,树状数组1差不多讲完了。。

树状数组1总代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std; const int maxn=; int n,m;
int tree[maxn<<]; int lowbit(int k){
return k&(-k);
} void update(int x,int k){
while(x<=n){
tree[x]+=k;
x+=lowbit(x);
}
} int query(int x){
int ans=;
while(x!=){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
} int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
int a;
scanf("%d",&a);
update(i,a);
}
for(int i=;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
if(a==)update(b,c);
else printf("%d\n",query(c)-query(b-));
}
}

接下来是树状数组2

有些不同。

 因为树状数组2变成了区间修改,单点询问,而区间修改如果用原来的方法会导致严重TLE。
那么这里我们就要用差分的方法来做这道题。
cha[i]表示a[i]-a[i-1]的值,特别的,cha[1]=a[1],因为我们设a[0]=0,那么我们,每一个数的值就可以用这个数的前缀和来表示。而这符合树状数组的求和方式。
对于区间修改,你只要修改两个值:
update(a,k);update(b+1,-k);
也就是把差分数组两边的值修改一下,区间的值就可以整体变化了。
代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
int read()
{
int ans=;
char last=' ',ch=getchar();
while(ch<''||ch>'')
{
last=ch,ch=getchar();
}
while(ch>=''&&ch<='')
{
ans=(ans<<)+(ans<<)+ch-'';
ch=getchar();
}
return last=='-'?-ans:ans;
}
int n,m,c[],before=,now,judge,a,b,k;
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int y)
{
for(;x<=n;x+=lowbit(x))c[x]+=y;
}
int sum(int x)
{
int ans=;
for(;x;x-=lowbit(x))ans+=c[x];
return ans;
}
int main(){
n=read();m=read();
for(int i=;i<=n;i++)
{
now=read();
update(i,now-before);//存入差分数组而不是原数组
before=now;
}
for(int i=;i<=m;i++)
{
judge=read();
if(judge==)
{
a=read(),b=read();k=read();
update(a,k);update(b+,-k);//不同的操作
}
else
{
a=read();
printf("%d\n",sum(a));
}
}
return ;
}

完结撒花!

洛谷P3374 【模板】树状数组 1&&P3368 【模板】树状数组 2题解的更多相关文章

  1. 洛谷P3374(线段树)(询问区间和,支持单点修改)

    洛谷P3374 //询问区间和,支持单点修改 #include <cstdio> using namespace std; ; struct treetype { int l,r,sum; ...

  2. 洛谷P1345 [USACO5.4]奶牛的电信Telecowmunication【最小割】分析+题解代码

    洛谷P1345 [USACO5.4]奶牛的电信Telecowmunication[最小割]分析+题解代码 题目描述 农夫约翰的奶牛们喜欢通过电邮保持联系,于是她们建立了一个奶牛电脑网络,以便互相交流. ...

  3. 洛谷 P3374 【模板】树状数组 1 题解

    P3374 [模板]树状数组 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N.M,分别表示该数列数字的个数 ...

  4. 洛谷P3759 [TJOI2017]不勤劳的图书管理员 【树状数组套主席树】

    题目链接 洛谷P3759 题解 树状数组套主席树板题 #include<algorithm> #include<iostream> #include<cstring> ...

  5. 【BZOJ2595_洛谷4294】[WC2008]游览计划(斯坦纳树_状压DP)

    上个月写的题qwq--突然想写篇博客 题目: 洛谷4294 分析: 斯坦纳树模板题. 简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点) ...

  6. 洛谷 P6177 - Count on a tree II/【模板】树分块(树分块)

    洛谷题面传送门 好家伙,在做这道题之前我甚至不知道有个东西叫树分块 树分块,说白了就是像对序列分块一样设一个阈值 \(B\),然后在树上随机撒 \(\dfrac{n}{B}\) 个关键点,满足任意一个 ...

  7. 洛谷 P6199 - [EER1]河童重工(点分治+虚树)

    洛谷题面传送门 神仙题. 首先看到这样两棵树的题目,我们肯定会往动态树分治的方向考虑.考虑每次找出 \(T_2\) 的重心进行点分治.然后考虑跨过分治中心的点对之间的连边情况.由于连边边权与两棵树都有 ...

  8. 「洛谷4197」「BZOJ3545」peak【线段树合并】

    题目链接 [洛谷] [BZOJ]没有权限号嘤嘤嘤.题号:3545 题解 窝不会克鲁斯卡尔重构树怎么办??? 可以离线乱搞. 我们将所有的操作全都存下来. 为了解决小于等于\(x\)的操作,那么我们按照 ...

  9. 「洛谷3870」「TJOI2009」开关【线段树】

    题目链接 [洛谷] 题解 来做一下水题来掩饰ZJOI2019考炸的心情QwQ. 很明显可以线段树. 维护两个值,\(Lazy\)懒标记表示当前区间是否需要翻转,\(s\)表示区间还有多少灯是亮着的. ...

  10. 洛谷P4299 首都(BZOJ3510)(LCT,树的重心,二分查找)

    Update:原来的洛谷U21715已成坑qwq 已经被某位管理员巨佬放进公共题库啦!又可以多一个AC记录啦! 洛谷题目传送门 其实也可以到这里交啦 思路分析 动态维护树的重心 题目中说到国家的首都会 ...

随机推荐

  1. django写原生sql语句

    执行自定义SQL语言: from django.db import connection ​ cursor=connection.cursor() ​ # 插入操作 cursor.execute(&q ...

  2. Java学习笔记之Iterator和ListIterator

    原文:https://blog.csdn.net/GongchuangSu/article/details/51514380 Iterator接口是对collection进行迭代的迭代器,ListIt ...

  3. zabbix图形刷新延迟解决

    环境: 服务端    ip :192.168.1.204       hostname:www.test.com 服务端    ip :192.168.1.206       hostname:www ...

  4. Unity中的动画系统和Timeline(5) Timeline

    在前面的动画,都是控制单独的物体,比如说控制一个角色的运动.而Timeline,可以对多个物体实施动画,形成过场动画,或者电影效果.比如,很多赛车游戏比赛开始前都会播放一段开场动画,围绕自己车的几个方 ...

  5. Python学习之面向对象(一)

    第六章 面向对象 6.1 面向对象的初识 6.1.1 什么是面向对象 面向过程式编程: ​ 好处:出色的完成所有的需求 ​ 坏处:凡是更改或者增加一条需求,可能整个项目都随之改变 面向对象式编程: 类 ...

  6. 【VS开发】【电子电路技术】RJ45以太网传输线研究

    RJ45以太网传输线研究 最近研究远距离差分视频传输方案,理所当然想到了LVDS协议.至于选用cameralink传输线,还是选用其他方案,本人更倾向于廉价的RJ45以太网线来实现LVDS差分信号的传 ...

  7. 使用new关键字创建对象数组(C#,C++,Java)

    今天遇到一个题目 分析下面的代码,判断代码是否有误. using System; namespace Test1 { class Point { public int x; public int y; ...

  8. CentOS7之root密码破解

    1.重新启动Linux系统,在出现引导界面时上下移动光标选择第一引导项,按下键盘的“e”键进入内核编辑界面,如图所示: 2.找到linux16参数这一行,按住“Ctrl+e”组合键跳转到行尾,添加rd ...

  9. BZOJ 1303 中位数图 题解

    题面 因为所求的是中位数,所以考虑改变原序列.把大于 b 的数全部变为 1,小于 b 的数变为 −1,等于 b 则为 0.问题就变为求存在几个包含 b的区间和为 0 . 根据乘法原理,我们枚举每一个l ...

  10. MySQL总结(5)

    视图 SELECT cust_name,cust_contact FROM customers,orders,orderitems WHERE customers.cust_id=orders.cus ...