当前位置: 首页 > news >正文

P13825 动态开店线段树

P13825 动态开店线段树

/*
普通的线段树中需要开 O(n) 的节点来存储信息,但是我们发现对于这道题而言,O(n) 的空间是根本开不下的,也就是我们不可能存储每个点的信息。但是真的所有点都有用吗?我们发现操作数 m 是很小的,根据线段树的复杂度分析,每次修改只会影响 O(logn) 个节点啊!也就是说,被修改操作影响的节点自始至终也只有 O(mlogn) 的量级,那么其余的点就根本不用建出来了。由此,我们得到一种处理方式:既然不是所有点都有用的,我只要等到它被用到的时候再把它开出来就好了。我们称这种方式为动态开点。不过我们不能直接通过 2x 和 2x+1 这样的形式得到左右儿子了,所以还需要记录每个点的左右儿子。
*/
#include<iostream>
#define int unsigned long longusing namespace std;const int N=1e5;
struct segmentree{
private:struct node{int l,r,sum,tag;};node tr[N*120+1];int idx=0;int build(){//新建一个节点++idx;tr[idx].l=tr[idx].r=tr[idx].sum=tr[idx].tag=0;return idx;}void pushup(int u){//上传更新tr[u].sum=tr[tr[u].l].sum+tr[tr[u].r].sum;}void pushdown(int u,int l,int r){//懒标记下传if(!tr[u].tag) return ;int mid=(l+r)>>1;if(!tr[u].l) tr[u].l=build();if(!tr[u].r) tr[u].r=build();tr[tr[u].l].sum+=(mid-l+1)*tr[u].tag;tr[tr[u].r].sum+=(r-mid)*tr[u].tag;tr[tr[u].l].tag+=tr[u].tag;tr[tr[u].r].tag+=tr[u].tag;tr[u].tag=0;}
public:int root=build();void modify(int u,int l,int r,int ql,int qr,int k){//区间加if(ql<=l && qr>=r){tr[u].sum+=(r-l+1)*k;tr[u].tag+=k;return ;}//查询区间完全覆盖当前区间int mid=(l+r)>>1;pushdown(u,l,r);if(ql<=mid){if(!tr[u].l) tr[u].l=build();modify(tr[u].l,l,mid,ql,qr,k);}//进入左子树查询if(qr>mid){if(!tr[u].r) tr[u].r=build();modify(tr[u].r,mid+1,r,ql,qr,k);}//进入右子树查询pushup(u);}int query(int u,int l,int r,int ql,int qr){//查询if(ql<=l && qr>=r) return tr[u].sum;int mid=(l+r)>>1;pushdown(u,l,r);int res=0;if(ql<=mid){if(!tr[u].l) tr[u].l=build();res+=query(tr[u].l,l,mid,ql,qr);}if(qr>mid){if(!tr[u].r) tr[u].r=build();res+=query(tr[u].r,mid+1,r,ql,qr);}return res;}
};
segmentree sgt;signed main(){cin.tie(nullptr)->sync_with_stdio(false);int n,m;cin>>n>>m;while(m--){int opt,l,r;cin>>opt>>l>>r;if(opt==1){int k;cin>>k;sgt.modify(sgt.root,1,n,l,r,k);}else{int tmp=(r-l+1)*(l+r)/2;//a[i]的初始值为i(线段树计算时未计算)cout<<sgt.query(sgt.root,1,n,l,r)+tmp<<'\n';}}return 0;
}