线段树(区间查修)

#define lc p<<1
#define rc p<<1|1
const int N =100005;
int n,m;vector<int>nums;
struct node{
    int l,r,sum,add;
}tr[4*N];
void pushup(int p){
     tr[p].sum=tr[lc].sum+tr[rc].sum;
}
void pushdown(int p){
     if(tr[p].add){
        tr[lc].sum+=tr[p].add*(tr[lc].r-tr[lc].l+1);//左子树的sum更新
        tr[rc].sum+=tr[p].add*(tr[rc].r-tr[rc].l+1);//右子树的sum更新
        tr[lc].add+=tr[p].add;
        tr[rc].add+=tr[p].add;
        tr[p].add=0;
        //更新完sum后更新懒标记
     }
}
void built(int p,int l,int r){//建树不需考虑懒标记
      tr[p]={l,r,nums[l]};//传入的参数仅仅是需要的东西,tr是为了建树
      if(l==r) return;//到底了 不需要向下查询了
      int m=l+r>>1;//获取分割点 便于分块
      built(lc,l,m);//左子树 lc是其左子树编号 实际区间为l--m
      built(rc,m+1,r);//右子树 同理 注意m+1
      pushup(p);//  // 合并左右子节点的统计值,更新父节点 p 的 sum
}
void change(int p,int x,int y,int k){//在区间(x,y)更改数据
     if(x<=tr[p].l&&tr[p].r<=y){//此节点的左右区间在内部
         tr[p].sum+=k*(tr[p].r-tr[p].l+1);
         tr[p].add+=k;//懒标记
         return;
     }
     int m=tr[p].l+tr[p].r>>1;//裂开之后会对儿子递归了 此时懒标记给下发过去
     pushdown(p);
     if(x<=m) change(lc,x,y,k);
     if(y>m) change(rc,x,y,k);
     pushup(p);//完成后全部上发过去更新
}
int query(int p,int x,int y){
   int sum=0;
   if(x<=tr[p].l&&tr[p].r<=y){
     return tr[p].sum;
   }
   int m=tr[p].l+tr[p].r>>1;//依旧分裂处理懒标记
   pushdown(p);
   if(x<=m) sum+=query(lc,x,y);
   if(y>m) sum+=query(rc,x,y);
   return sum;
}
void solve(){
    cin>>n>>m;
    nums.resize(n+1);
    for(int i=1;i<=n;i++)cin>>nums[i];
    built(1,1,n);
    while(m--){
       int x;cin>>x;
       if(x==1){
        int l,r,k;cin>>l>>r>>k;
          change(1,l,r,k);
       }else{
         int l,r;cin>>l>>r;
         cout<<query(1,l,r)<<endl;
       }
    }
} 
posted @ 2025-06-09 15:43  Qacter  阅读(13)  评论(0)    收藏  举报