对于区间的单点处理:
#include<iostream> using namespace std; const int N=1000010; typedef long long ll; ll a[N<<2],tree[N<<2];//首先对于数组我们要开正常数组的四倍之多 void build_tree(int node,int begin,int end){ //建树 if(begin==end){ tree[node]=a[begin]; return; } int mid=(begin+end)>>1; int left_node=2*node+1; int right_node=2*node+2; build_tree(left_node,begin,mid); build_tree(right_node,mid+1,end); tree[node]=tree[left_node]+tree[right_node]; //pushup父结点的情况 } void update_tree(int node,int begin,int end,ll index,ll val){ //更新树上各点的数值 if(begin==end){ //边界条件时进行更新 a[index]=val; tree[node]=val; return; } int mid=(begin+end)>>1; int left_node=2*node+1; int right_node=2*node+2; //判断该进行左递归还是右递归 if(index<=mid&&index>=begin){ update_tree(left_node,begin,mid,index,val); } else update_tree(right_node,mid+1,end,index,val); tree[node]=tree[left_node]+tree[right_node]; } ll query_tree(int node,int begin,int end,int L,int R){ //递归出口一般来说有三个,如果查询区间不在当前的范围内,那么就返回0 //查询区间如果完全包含在当前的区间范围内,那么就不需进行向下递归 //如果当前的区间长度为1时,也可以作为出口 if(L>end||R<begin) return 0; else if(begin==end) return tree[node]; else if(L<=begin&&R>=end) return tree[node]; int mid=(begin+end)>>1; int left_node=2*node+1; int right_node=2*node+2; ll left_sum=query_tree(left_node,begin,mid,L,R); ll right_sum=query_tree(right_node,mid+1,end,L,R); return left_sum+right_sum; } int main(){ int n,q; cin>>n>>q; //这里要注意,我们的下标是从0开始的 for(int i=0;i<n;i++) cin>>a[i]; build_tree(0,0,n-1); while(q--){ int t; cin>>t; if(t==1){ ll i,x; cin>>i>>x; x+=a[i-1]; update_tree(0,0,n-1,i-1,x); } else if(t==2){ ll l,r; cin>>l>>r; ll ans=query_tree(0,0,n-1,l-1,r-1); cout<<ans<<endl; } } return 0; }
区间修改的模板:
#include<iostream> using namespace std; typedef long long ll; const int maxn=1000010; ll n,m,a[maxn],tag[maxn<<2],ans[maxn<<2]; //tag数组是用来作为懒标记的 inline ll ls(ll p){ return p<<1; } inline ll rs(ll p){ return p<<1|1; } inline void push_up(ll p){ ans[p]=ans[ls(p)]+ans[rs(p)]; } //pushup目的是为了维护父子结点的逻辑关系 void build(ll p,ll l,ll r){ tag[p]=0; //这是初始的懒标记设为0 if(l==r){ ans[p]=a[l]; return; } ll mid=(l+r)>>1; build(ls(p),l,mid); build(rs(p),mid+1,r); push_up(p); } inline void f(ll p,ll l,ll r,ll k){ tag[p]=tag[p]+k; ans[p]+=k*(r-l+1); } //向下递归更新懒标记 inline void push_down(ll p,ll l,ll r){ ll mid=(l+r)>>1; f(ls(p),l,mid,tag[p]); f(rs(p),mid+1,r,tag[p]); tag[p]=0; } inline void update(ll nl,ll nr,ll l,ll r,ll p,ll k){ //nl,nr为要修改的区间,l,r,p为当前的区间端点和当前的结点编号 if(nl<=l&&r<=nr){ ans[p]+=k*(r-l+1); tag[p]+=k; return; } push_down(p,l,r); //回溯pushup之前是 向下传递的,自然要每个结点都更新到 ll mid=(l+r)>>1; if(nl<=mid) update(nl,nr,l,mid,ls(p),k); if(nr>mid) update(nl,nr,mid+1,r,rs(p),k); push_up(p);//向上进行回溯更新父结点 } inline ll query(ll q_x,ll q_y,ll l,ll r,ll p){ //q_x,q_y为要查询区间的端点 ll res=0; if(q_x<=l&&r<=q_y) return ans[p]; ll mid=(l+r)>>1; push_down(p,l,r); if(q_x<=mid) res+=query(q_x,q_y,l,mid,ls(p)); if(q_y>mid) res+=query(q_x,q_y,mid+1,r,rs(p)); return res; } int main(){ std::ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL); cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; build(1,1,n); while(m--){ int t; cin>>t; if(t==1){ ll i,x; cin>>i>>x; update(i,i,1,n,1,x); } else{ ll x,y; cin>>x>>y; ll ans=query(x,y,1,n,1); cout<<ans<<endl; } } return 0; }