图片来自度娘~~

树状数组形如上图,是一种快速查找区间和,快速修改的一种数据结构,一个查询和修改复杂度都为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. 浏览器端-W3School-HTML:HTML DOM Select 对象

    ylbtech-浏览器端-W3School-HTML:HTML DOM Select 对象 1.返回顶部 1. HTML DOM Select 对象 Select 对象 Select 对象代表 HTM ...

  2. python接口测试之mock(二)

    上一篇对mock-server已经做了初步的介绍,今天这里继续接着之前的介绍进行,我们先看之前的mock-server部分,之前编写了一个登录的mock,具体json文件见如下的内容: 小王子1110 ...

  3. flutter 隐藏返回按钮 自定义返回按钮

    自定义返回按钮 //改变颜色 Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: BackBu ...

  4. rocketMQ 订阅关系

    场景:2 个消费者进程中,创建了 2 个消费者,同属于 1 个消费组,但是订阅了不同的 topic,会因为订阅信息相互覆盖,导致拉不到消息. 原因是 rocketMQ 的订阅关系,是根据 group ...

  5. Linux_RHEL_设置网络

    目录 目录 Selinux Iptable NetworkManager 基本网络配置 编辑网卡子接口 ip指令 ifconfig指令 修改主机名 服务端口 Selinux 是Linux的一种权限管理 ...

  6. C#, 计算字符串里有多少个指定字符

    int number = a.Count<char>(c => c == '@');

  7. nodejs+koa2 实现一个get请求

    html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  8. SpringBoot自动化配置之二:自动配置(AutoConfigure)原理、EnableAutoConfiguration、condition

    自动配置绝对算得上是Spring Boot的最大亮点,完美的展示了CoC约定优于配置: Spring Boot能自动配置Spring各种子项目(Spring MVC, Spring Security, ...

  9. Linux mv命令(7)

    mv命令,move的缩写,顾名思义是移动文件的意思.其实就相当于剪切操作,而前面说的cp命令,就是复制粘贴,这两个有什么区别想必不用多说. 基本使用 使用格式 mv 源文件 目标文件 我的根目录下有 ...

  10. 初步学习jquery学习笔记(一)

    什么是jquery? Jquery是javascript的一个函数库包含以下功能: html元素选取 html元素的操作 css操作 html事件的函数 javacript的特效 html的遍历和修改 ...