【关于动态开点线段树】
动态开点线段树
首先:
你需要知道 什么是线段树(我的模板) 及 权值线段树
动态开点的概念和功能
在线段树里 其实 动态开点 就和 C++ 里的new()差不多
思考这样一个问题
我们有一个权值最大为\(1^9\)的权值线段树
我们一算 需要的空间元素个数大概在 \(4^9\) 直接MLE
于是你说着出题人毒瘤
但其实你是不会动态开点
我们在寻常的线段树中是创造了一个满二叉树 对于一个节点\(x\) 他的左儿子是 \(x * 2\) 他的右儿子是 \(x * 2 + 1\)
但是如果一个节点创建后没有查询 那么创建它只会增大空间
所以我们采用动态开点
只有在一个点被用时 创建它
就是节点的编号和节点左右儿子的编号都是乱序的,是我们临到使用之时现加上去的。
代码
ll Get_Son(long long &pos)
{
if(pos==0) pos=++cnt;
return pos;
}
void PushUp(long long pos)
{
sum[pos]=sum[lson[pos]]+sum[rson[pos]];
}
void PushDown(long long pos,long long l,long long r)//区间查询用
{
sum[Get_Son(lson[pos])]+=(mid-l+1)*Lazy[pos];
sum[Get_Son(rson[pos])]+=(r-mid)*Lazy[pos];
Lazy[lson[pos]]+=Lazy[pos];
Lazy[rson[pos]]+=Lazy[pos];
Lazy[pos]=0;
}
void UpZone(long long &pos,long long l,long long r,long long L,long long R,long long C)
{
//L,R表示操作区间 , l,r表示当前节点区间 , pos表示当前节点编号
if(pos==0) pos=++cnt;
if(Lazy[pos]!=0) PushDown(pos,l,r);//下推标记
if(L<=l && R>=r)//节点区间在操作区间之内,直接返回
{
sum[pos]+=(r-l+1)*C;//这个点需要加上区间长度*C
Lazy[pos]+=C;//用Lazy标记,表示本区间的Sum正确,子区间的Sum仍需要根据Lazy调整
return;
}
if(L<=mid) UpZone(lson[pos],l,mid,L,R,C);
if(R>mid) UpZone(rson[pos],mid+1,r,L,R,C);
PushUp(pos);
}
ll Query(long long pos,long long l,long long r,long long L,long long R)
{
//L,R表示操作区间 , l,r表示当前节点区间 , pos表示当前节点编号
if(pos==0) return 0;
if(Lazy[pos]) PushDown(pos,l,r);//下推标记,否则sum可能不正确
if(L<=l && R>=r)//节点区间在操作区间之内,直接返回
{
return sum[pos];
}
//统计答案
long long ans=0;
if(L<=mid) ans+=Query(lson[pos],l,mid,L,R);
if(R>mid) ans+=Query(rson[pos],mid+1,r,L,R);
PushUp(pos);
return ans;
}