动态开点线段树

在面对数据范围比较大,但是实际上并非范围中的每个点都要用到的时候,我们可以采取动态开点的策略,也就是用到哪个开哪个

e g . eg. eg. 已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 k k k
  2. 求出某区间的和
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
ll sz[maxn<<1],lazy[maxn<<1];
int L[maxn<<1],R[maxn<<1];
int cnt=1;
int n,m;
inline void Pushup(int &rt){}
inline void Pushdown(int k,int l,int r){
    if(lazy[k]){
        if(!L[k])L[k]=++cnt;
        if(!R[k])R[k]=++cnt;
        lazy[L[k]]+=lazy[k];
        lazy[R[k]]+=lazy[k];
        int mid=(l+r)>>1;
        sz[L[k]]+=(mid-l+1)*lazy[k];
        sz[R[k]]+=(r-mid)*lazy[k];
        lazy[k]=0;
    }
}
inline void build(int &rt,int l,int r,int pos,int x){
    if(!rt)rt=++cnt;//动态开点
    if(l==r){
    	sz[rt]=x;return;
	}
    int mid=(l+r)>>1;
    if(pos<=mid)build(L[rt],l,mid,pos,x);
    else build(R[rt],mid+1,r,pos,x);
    sz[rt]=sz[L[rt]]+sz[R[rt]];
}
inline void update(int &rt,int l,int r,int lpos,int rpos,int x){
    if(!rt)rt=++cnt;
    if(lpos<=l&&rpos>=r){
        lazy[rt]+=x;sz[rt]+=(r-l+1)*x;
        return;
    }
    Pushdown(rt,l,r);
    int mid=(l+r)>>1;
    if(lpos<=mid)update(L[rt],l,mid,lpos,rpos,x);
    if(rpos>mid)update(R[rt],mid+1,r,lpos,rpos,x);
    sz[rt]=sz[L[rt]]+sz[R[rt]];
}
inline ll query(int rt,int l,int r,int lpos,int rpos){
    if(!rt)return 0;
    if(lpos<=l&&rpos>=r)return sz[rt];
    Pushdown(rt,l,r);
    int mid=(l+r)>>1;
    ll ans=0;
    if(lpos<=mid)ans+=query(L[rt],l,mid,lpos,rpos);
    if(rpos>mid)ans+=query(R[rt],mid+1,r,lpos,rpos);
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int x;int tmp=1;
        scanf("%d",&x);
        build(tmp,1,n,i,x);
    }
    for(int i=1;i<=m;i++){
        int q;
        scanf("%d",&q);
        if(q==1){
            int lpos,rpos,x;
            scanf("%d%d%d",&lpos,&rpos,&x);
            int temp=1;
            update(temp,1,n,lpos,rpos,x);
        }else{
            int lpos,rpos;
            scanf("%d%d",&lpos,&rpos);
            printf("%lld\n",query(1,1,n,lpos,rpos));
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值