/*
线段树(Segment Tree)
*/
struct SegmentTree
{
static const int N=1e5+5;
struct node{
int l,r; //[l,r]表示节点所代表的区间范围
int sum; //sum维护该区间内的元素和
int tag; //延迟标记
};
node t[N<<2];
void pushup(int p){ //区间合并,向上拓展,p代表当前节点的编号
int lson=p*2,rson=p*2+1;
t[p].sum=t[lson].sum+t[rson].sum; //sum[l,r]=sum[l,mid]+sum[mid+1,r]
}
void pushdown(int p){ //将父节点的状态向下传递(延迟标记的传递)
if(t[p].tag){ //说明父节点的状态已被改变
int mid=(t[p].l+t[p].r)/2;
int lson=p*2,rson=p*2+1;
t[lson].tag+=t[p].tag; //注意这里一定是要累加,因为该区间有可能被父节点多次更新
t[rson].tag+=t[p].tag;
t[lson].sum+=(t[lson].r-t[lson].l+1)*t[p].tag;
t[rson].sum+=(t[rson].r-t[rson].l+1)*t[p].tag;
t[p].tag=0;//父节点传递完成后标志复原
}
}
//建树
void build(int p,int l,int r){
t[p].l=l; //确定节点表示的范围
t[p].r=r;
t[p].tag=0;
if(l==r){ //如果到达叶节点
t[p].sum=1; //节点的值更新为原序列中的元素值
return;
}
int mid=(l+r)/2;
int lson=p*2,rson=p*2+1;
build(lson,l,mid); //递归左子节点
build(rson,mid+1,r); //递归右子节点
pushup(p); //递归返回后进行上推,更新本节点信息
}
//单点修改
void updateOne(int p,int pos,int num){
if(t[p].l==t[p].r){ //到达叶节点
t[p].sum+=num;
return;
}
int mid=(t[p].l+t[p].r)/2;
int lson=p*2,rson=p*2+1;
if(pos<=mid){ //pos在左子节点的区间内
updateOne(lson,pos,num);
}
else{ //pos在左子节点的区间内
updateOne(rson,pos,num);
}
pushup(p); //递归返回后进行上推,更新本节点信息
}
//区间修改,[L,R]表示待修改区间
void update(int p,int L,int R,int num){
if(L<=t[p].l&&R>=t[p].r){ //区间被完全覆盖
t[p].tag+=num; //这里累加,原因同pushdown
t[p].sum+=(t[p].r-t[p].l+1)*num;
return; //由于此处直接返回,所以需要延迟标记,此处未更新该节点的子树
}
pushdown(p); //进入子节点前先传递延迟标记,便于接下来的递归更新
int mid=(t[p].l+t[p].r)/2;
int lson=p*2,rson=p*2+1;
if(L<=mid){ //待修改区间与左子节点有重叠
update(lson,L,R,num);
}
if(R>mid){ //待修改区间与右子节点有重叠
update(rson,L,R,num);
}
pushup(p);//子节点更新返回后要更新父节点
}
//区间查询
int query(int p,int L,int R){
if(L<=t[p].l&&R>=t[p].r){ //区间被完全覆盖
return t[p].sum; //直接返回该区间的和
}
int tsum=0;
int mid=(t[p].l+t[p].r)/2;
int lson=p*2,rson=p*2+1;
pushdown(p); //在向下访问之前,先向下更新
if(L<=mid){ //待查询区间与左子节点有重叠
tsum+=query(lson,L,R);
}
if(R>mid){ //待查询区间与右子节点有重叠
tsum+=query(rson,L,R);
}
return tsum;
}
};