• 「模板」线段树静态开点(单点+区间修改)、动态开点


    相关讲解资料:
     

    树状数组:https://blog.csdn.net/qq_34374664/article/details/52787481 (线段树预备)

    线段树讲解:
     
    代码:
     

    完整注释模板一张,参(chao)考(xi)楼上的博客

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    
    #define mid  (l+r)/2
    #define lson (pos<<1)
    #define rson ((pos<<1)|1)
    #define maxn 100007 //元素个数
    
    ll n,m;
    ll root=1;
    ll arr[maxn];
    ll Lazy[maxn<<2];//区间增加的lazy标记
    /*其目的是:
                为防止修改区间总结点对每个子节点都要进行修改,导致复杂度爆炸
                暂时记录一下这个区间总结点的所有子树都“待修改”
                如果用到下面的子节点就修改,下推lazy标志,用不到就不管
                以此来减少复杂度
    */
    ll sum[maxn<<2];//线段树求和最多分成4个子区间
    
    void PushUp(long long pos)//暂时写成求和函数,可以自由变换
    {
        sum[pos]=sum[lson]+sum[rson];
        //用数组表示二叉树:假设某个节点的编号为v,那么它的左子节点编号为2*v,右子节点编号为2*v+1,规定根节点为1
        //通常2*v写成v<<1 , 2*v+1写成v<<1|1;
    }
    
    void PushDown(long long pos,long long l,long long r)//区间查询用
    {
        //l,r为左子树,右子树的数字区间
        if(Lazy[pos])
        {
            //修改子节点的增加数 
            Lazy[lson]+=Lazy[pos];
            Lazy[rson]+=Lazy[pos];
            //修改子节点区间的sum
            sum[lson]+=Lazy[pos]*(mid-l+1);
            sum[rson]+=Lazy[pos]*(r-(mid+1)+1);
            //清除本节点标记
            Lazy[pos]=0;
        }
    }
    
    void Build(long long l,long long r,long long pos)//[l,r]表示当前节点区间,pos表示当前节点的实际存储位置
    {
        if(l==r)//如果到达儿子节点,存储并返回
        {
            sum[pos]=arr[l];
            return;
        }
        Build(l,mid,pos<<1);
        Build(mid+1,r,pos<<1|1);
        PushUp(pos);
    }
    
    void UpPoint(long long pos,long long l,long long r,long long L,long long C)//对单点修改
    {
        //L表示要修改的点编号,[l,r]表示当前区间,pos是当前节点编号;
        if(l==r)//到达儿子节点之后就修改
        {
            sum[pos]+=C;
            return;
        }
        //根据条件判断往左子树调用还是往右
        if(L<=mid) UpPoint(lson,l,mid,L,C);
        else UpPoint(rson,mid+1,r,L,C);
        PushUp(pos);//子节点更新之后本节点也需要更新;
    }
    
    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(L<=l && R>=r)//节点区间在操作区间之内,直接返回
        {
            sum[pos]+=C*(r-l+1);//这个点需要加上区间长度*C
            Lazy[pos]+=C;//用Lazy标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add调整
            return;
        }
        PushDown(pos,l,r);//下推标记
        if(L<=mid) UpZone(lson,l,mid,L,R,C);
        if(R>mid) UpZone(rson,mid+1,r,L,R,C);
        PushUp(pos);
    }
    
    ll Query(long long l,long long r,long long L,long long R,long long pos)
    {
        //L,R表示操作区间 , l,r表示当前节点区间 , pos表示当前节点编号
        if(L<=l && R>=r)//节点区间在操作区间之内,直接返回
        {
            return sum[pos];
        }
        PushDown(pos,l,r);//下推标记,否则sum可能不正确
        
        //统计答案
        long long ans=0;
        if(L<=mid) ans+=Query(l,mid,L,R,lson);
        if(R>mid) ans+=Query(mid+1,r,L,R,rson);
        PushUp(pos);
        return ans;
    }
    
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            long long tmp;
            cin>>tmp;
            UpZone(root,1,n,i,i,tmp);
        }
        for(int j=1;j<=m;j++)
        {
            long long a,b,c,d;
            cin>>a;
            if(a==1)
            {
                cin>>b>>c>>d;
                UpZone(root,1,n,b,c,d);
            }
            else
            {
                cin>>b>>c;
                cout<<Query(1,n,b,c,root)<<endl;
            }
        }
        return 0;
    }

    下面是动态开点的模板:

    1. 不能define lson,rson,也不能用pos<<1和pos<<1|1,否则就失去了“动态开点”的意义
    2. Get_Son和UpZone要&引用
    3. 尽量开long long,也好调

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    
    //!!!!!!!!!
    //Get_Son和UpZone要&引用
    //不能define  lson,rson,也不能用pos<<1和pos<<1|1
    //!!!!!!!!!
    
    #define mid  (l+r)/2
    #define maxn 1000007 //元素个数
    
    ll n,m;
    ll root=1,cnt=1;
    ll lson[maxn],rson[maxn];
    ll Lazy[maxn<<2];//区间增加的lazy标记
    /*其目的是:
                为防止修改区间总结点对每个子节点都要进行修改,导致复杂度爆炸
                暂时记录一下这个区间总结点的所有子树都“待修改”
                如果用到下面的子节点就修改,下推lazy标志,用不到就不管
                以此来减少复杂度
    */
    ll sum[maxn<<2];//线段树求和最多分成4个子区间
    
    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]];
        //用数组表示二叉树:假设某个节点的编号为v,那么它的左子节点编号为2*v,右子节点编号为2*v+1,规定根节点为1
        //通常2*v写成v<<1 , 2*v+1写成v<<1|1;
    }
    
    void PushDown(long long pos,long long l,long long r)//区间查询用
    {
        //l,r为左子树,右子树的数字区间
    
        // if(Lazy[pos]==0) return;
        // if(r-l<=1) return;
        // if(pos<<1!=0)
        // {
        //     pos<<1=++cnt;
        //     sum[pos<<1]+=(mid-l+1)*Lazy[pos];
        //     Lazy[pos<<1]+=Lazy[pos];
        // }
        // if(rson[pos]!=0)
        // {
        //     rson[pos]=++cnt;
        //     sum[rson[pos]]+=(r-mid+1)*Lazy[pos];
        //     Lazy[rson[pos]]+=Lazy[pos];
        // }
        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;
    }
    
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            int tmp;
            cin>>tmp;
            UpZone(root,1,n,i,i,tmp);
        }
        for(int j=1;j<=m;j++)
        {
            int a,b,c,d;
            cin>>a;
            if(a==1)
            {
                cin>>b>>c>>d;
                UpZone(root,1,n,b,c,d);
            }
            else
            {
                cin>>b>>c;
                cout<<Query(root,1,n,b,c)<<endl;
            }
        }
        return 0;
    }
  • 相关阅读:
    C# 反射数据库数据过程中值类型存在DBNull的处理方法 == System.DBNull.Value
    Git 未能顺利结束 (退出码 128)解决办法 git常用命令流程图
    IDEA之The directory xxxxx is under Git, but is not registered in the Settings.
    git push提交成功后如何撤销回退 我是手动修改再提交一次 。。或者IDEA有代码修改历史记录功能 进行回退
    IDEA,发现Project文件目录不见了,只剩External Libraries的原因
    分布式事务 seata
    idea设置类,方法模板 阿里检测
    随笔
    初学一点点空间分解和有理标准型
    Vue2+Cesium1.9+热力图开发笔记
  • 原文地址:https://www.cnblogs.com/LocaEtric/p/9614245.html
Copyright © 2020-2023  润新知