【关于动态开点线段树】

动态开点线段树

首先:

你需要知道 什么是线段树(我的模板) 及 权值线段树

动态开点的概念和功能

在线段树里 其实 动态开点 就和 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;
}
原文地址:https://www.cnblogs.com/dixiao/p/13744221.html