题目来源
洛谷P3368【模板】树状数组2https://www.luogu.org/problem/show?pid=3368
思路
按照差分思想 叶节点记录这个数与前一个数的差
节点记录左右儿子(l,r)、区间范围(x,y)、区间和(num)
查询x时 递归查找1~x区间和即为数列中第x个数的值
修改x~y的节点加k时 将第x个点加k、第y+1个点减k(即加-k)即可
将第i个点n加v时 对由代表i~i区间的叶节点到根节点的路径上的节点的num进行修改
代码(C++)
#include <cstdio>
using namespace std;
int n,m,c,u,v,w; long long pre=0;
struct tree
{
tree *l,*r;
int x,y;
long long num;
}*root,*null=new tree();
tree* build(int st,int en);
long long ask(int x,tree *pos);
void change(int x,long long k,tree *pos);
int main()
{
scanf("%d%d",&n,&m);
root=build(1,n);
for(int i=1;i<=m;++i)
{
scanf("%d",&c);
if(c==1)
{
scanf("%d%d%lld",&u,&v,&w);
change(u,w,root);
if(v+1<=n)
change(v+1,-w,root);
}
else
{
scanf("%d",&u);
printf("%lld\n",ask(u,root));
}
}
return 0;
}
long long ask(int x,tree *pos)
{
if(x==pos->y)
return pos->num;
if(x>=pos->r->x)
return ask(x,pos->r)+pos->l->num;
else
return ask(x,pos->l);
}
void change(int x,long long k,tree *pos)
{
if(pos->l==null&&pos->y==x)
{
pos->num+=k;
return ;
}
if(x<=pos->l->y)
change(x,k,pos->l);
else
change(x,k,pos->r);
pos->num=pos->l->num+pos->r->num;
return ;
}
tree* build(int st,int en)
{
tree *pos=new tree();
pos->x=st; pos->y=en;
if(st==en)
{
pos->l=null; pos->r=null;
scanf("%lld",&pos->num);
pos->num-=pre;
pre+=pos->num;
}
else
{
pos->l=build(st,(st+en)/2);
pos->r=build((st+en)/2+1,en);
pos->num=pos->l->num+pos->r->num;
}
return pos;
}