动态开点线段树
应用场景
对于数据量比较大的题目,为了降低空间复杂度,可以不用建出整颗线段树的结构,而是在最初只建立一个根节点,代表整个区间。当需要访问线段树的某棵子树(某个子区间)时,再建立代表这个子区间的节点。采用这种方法维护的线段树称为动态开点的线段树。
代码模板(区间求和为例)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 300010;
int n,q;
struct Node
{
int ls, rs;
ll sum, lazy;
}tr[N];
int root, cnt;
void insert(int &root,int l,int r,int ql,int qr,int x)
{
if(!root) root = ++ cnt;
ll b = min(r,qr) - max(l,ql) + 1;
tr[root].sum += b * x;
if(l>=ql&&r<=qr){
tr[root].lazy += x;
return;
}
else{
int mid = l + r >> 1;
if(ql<=mid) insert(tr[root].ls,l,mid,ql,qr,x);
if(qr>mid) insert(tr[root].rs,mid+1,r,ql,qr,x);
}
}
ll query(int root,int l,int r,int ql,int qr)
{
if(l>=ql&&r<=qr) return tr[root].sum;
int mid = l + r >> 1;
if(tr[root].lazy){
if(!tr[root].ls) tr[root].ls = ++cnt;
tr[tr[root].ls].sum += (mid - l + 1) * tr[root].lazy;
tr[tr[root].ls].lazy += tr[root].lazy;
if(!tr[root].rs) tr[root].rs = ++cnt;
tr[tr[root].rs].sum += (r - mid) * tr[root].lazy;
tr[tr[root].rs].lazy += tr[root].lazy;
tr[root].lazy = 0;
}
ll ans = 0;
if(ql<=mid) ans += query(tr[root].ls,l,mid,ql,qr);
if(qr>mid) ans += query(tr[root].rs,mid+1,r,ql,qr);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++){
ll x;
scanf("%lld",&x);
insert(root,1,n,i,i,x);
}
while(q--){
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
if(op==1){
ll x;
scanf("%lld",&x);
insert(root,1,n,l,r,x);
}
else printf("%lld\n",query(root,1,n,l,r));
}
return 0;
}