主要是清楚lowbit的用法
树状数组模板题
int lowbit(int x){//返回x二进制中首次出现1的位置所代表的值,如(当x=6时返回2)
return x&(-x);//利用原码和补码的关系
}
树状数组的形成示意图:
图中我们可以发现2的二进制是010所以它的lowbit值也为2即表明c2可以将A2与A1两个元素联系起来即c2为A【1,2】的区间和,同理c4可以联系4个(自己与前个),c6可以联系两个(因为lowbit(110)==2)即自己和A5.
由此可以建立树状数组:
void build(int t){
for(int i=1;i<=t;i++){
for(int j=i;j>=i-lowbit(i)+1;j--){
ans[i]+=A[j];
}
}
}
树状数组的单点修该在对应点本修改时也要修改其祖先,如改节点1时,c2,c4,c8等区间和也要对应改变,通过观察下标可以发现lowbit(1)+lowbit(1)=2,lowbit(2)+lowbit(2)=4,也就是说节点的父亲节点的下标等于子节点下标加子节点下标的lowbit值,从而可以得出单点修改代码:
void xiugai(int a,int b){
while(a<=n){
ans[a]+=b;
a+=lowbit(a);
}
}
树状数组区间求和也是通过观察其下标规律,如:若求A【1,7】的值,即求c7+c6+c4,其中7(111),6(110),4(100),可以发现:
6=7-lowbit(7),4=6-lowbit(6),直到4-lowbit(4)=0终止:
由此可以得出区间求和代码:
ll sum(int a){
ll t=0;
while(a>=1){
t+=ans[a];
a-=lowbit(a);
}
return t;
}
最后附完整代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll A[500005],ans[500005]; ll n,m;
int lowbit(int x){
return x&(-x);
}
void build(int t){
for(int i=1;i<=t;i++){
for(int j=i;j>=i-lowbit(i)+1;j--){
ans[i]+=A[j];
}
}
}
ll sum(int a){
ll t=0;
while(a>=1){
t+=ans[a];
a-=lowbit(a);
}
return t;
}
void xiugai(int a,int b){
while(a<=n){
ans[a]+=b;
a+=lowbit(a);
}
}
void chaxun(int x){
if(x==1){
int a,b; cin>>a>>b;
xiugai(a,b);
}
if(x==2){
int a,b; cin>>a>>b;
ll t=sum(b)-sum(a-1);//利用前缀和思想求区间和
cout<<t<<endl;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>A[i];
}
build(n);
for(int i=0;i<m;i++){
int e; cin>>e;
chaxun(e);
}
}