树状数组入门
含义:顾名思义,用树状表示的数组
功能:是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
如图所示:
数组a[i]表示叶子节点
c[i]表示叶子节点的权值之和
根据上图可以得到下面的各式子
c[1]=a[1]
c[2]=a[1]+a[2]
c[3]=a[3]
c[4]=a[1]+a[2]+a[3]+a[4]
c[5]=a[5]
c[6]=a[5]+a[6]
c[7]=a[7]
c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
那么我们怎么知道要加几个数呢?它有没有啥规律呢?
这时候引入一个数k:表示二进制下i的二进制中从最低位到高位连续零的长度
每个c[i]需要加2k个数: 即c[i]=a[i]+a[i-1]+…a[i-2k]
怎么能很直观的知道2k是多少呢
这里我们引入一个lowbit()函数
int lowbit(int i){
return i&(-i);
}
可以写个简单的程序感受一下
#include<iostream>
using namespace std;
int lowbit(int i){
return i&(-i);
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
cout<<lowbit(i)<<endl;
}
return 0;
}
题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某一个数加上x
2.求出某区间每一个数的和
输入格式
第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3个整数,表示一个操作,具体如下:
操作1: 格式:1 x k 含义:将第x个数加上k
操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和
输出格式
输出包含若干行整数,即为所有操作2的结果。
输入
5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4
输出
14
16
#include<iostream>
using namespace std;
const int maxn=2e6+5;
int n,m;
int tree[maxn];
int lowbit(int i){//lowbit()
return i&(-i);
}
void add(int x,int k){//给第i位加k
while(x<=n){
tree[x]+=k;//第x位加k
x+=lowbit(x);//相应的区间包括第x位的都要加k
}
}
int sum(int x){
int ans=0;//ans表示前x项的和
while(x!=0){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
int a;
cin>>a;
add(i,a);
}
for(int i=1;i<=m;i++){
int a,b,c;
cin>>a>>b>>c;
if(a==1){
add(b,c);
}else if(a==2){
cout<<sum(c)-sum(b-1)<<endl;//[b,c]=前c位的和-前(b-1)位的和
}
}
return 0;
}