zoukankan      html  css  js  c++  java
  • 线段树

    线段树主要用于动态修改某个/区间的值,求某个/区间的和,区间最大/小值,等等跟区间有关的都可以尝试用线段树

    线段树的操作都是在lgn内完成的

    例如:输入n<1e5,然后输入n个数,q次询问,每次询问先输入opt

    如果opt=1,接下来输入l,r,v,代表l~r区间的数都加上v

    如果opt=2,接下来输入l,r,让你输出lr位置的数字的和是多少

    如果暴力需要n2,但是线段树可以实现为nlgn

     

    线段树主要就建了一个二叉树,每个点都存储一个区间的值,然后每个节点有两个子节点(除了区间大小是1的),一半一半的存储本节点的区间值,所以每一层(最后一层可能会有差的)都能不重不漏的存储整个1~n的值

    由于每次都是一半一半分割,因此树的高度就是lgn了,那么查询任何一个点都可以在lgn内找到那个点

    对于任何一个区间,都可以由不到2*lgn的节点表示(因为区间连续,而且要不重不漏,所以不存在父子节点和兄弟节点同时标记,那么相当于每层最多只有两个)

    一般来说数组都是开成正常数组的4倍,空间是够得

    其中a[i]数组代表第i个位置的值是多少

    f[i]数组代表,第所指向的区间的值之和(有时候可以改成最大值,最小值,看需求是什么了)

    yc[i]数组用于延迟,当yc[i]不是0的时候,代表子区间的值需要更新,那么当要用到子区间的时候,就顺便更新即可。

    函数rt是节点在f数组中下标,ls,rs是本节点左孩子节点和右孩子节点在数组中的下标,

    L,R代表这个节点所管辖的区间,l,r是要操作的区间,

    #include<stdio.h>
    
    #define rep(i,j,k) for(int i=j;i<=k;++i)
    
    #define ls rt<<1
    
    #define rs rt<<1|1
    
    #define N 4*100005
    
    int a[N],f[N],yc[N];
    
    void build(int rt,int L,int R)
    
    {
    
        yc[rt]=0;
    
        if(L==R)
    
        {
    
            f[rt]=a[L];///记录这个区间的和,只有一个点时,就是这个点的值
    
            return ;
    
        }
    
       int mid=(L+R)/2;///每次都分成一半
    
       build(ls,L,mid);
    
       build(rs,mid+1,R);
    
       f[rt]=f[ls]+f[rs];
    
    }
    
    void up(int rt,int L,int mid,int R)///用来把延迟标记去掉,更新f数组
    
    {
    
        if(yc[rt])///代表需要更新
    
        {
    
            f[ls]+=yc[rt]*(mid-L+1);
    
            f[rs]+=yc[rt]*(R-mid);
    
            yc[ls]+=yc[rt];
    
            yc[rs]+=yc[rt];
    
            yc[rt]=0;
    
        }
    
    }
    
    void update(int rt,int L,int R,int l,int r,int v)
    
    {
    
        if(l<=L&&R<=r)///因为这个区间是修改的子区间,所以加个延迟标记,以后要更新子区间的时候直接一起更新就好
    
        {
    
            f[rt]+=v*(R-L+1);///虽然有延迟标记,但是延迟标记只能记录他的所有子区间,本个区间的f要一直更新好
    
            yc[rt]+=v;
    
            return ;
    
        }
    
        int mid=(L+R)/2;
    
        up(rt,L,mid,R);///去掉延迟
    
        if(l<=mid)///代表左孩子的区间中含有更新的区间
    
            update(ls,L,mid,l,r,v);
    
        if(r>=mid+1)///代表右孩子的区间中含有更新的区间
    
            update(rs,mid+1,R,l,r,v);
    
        f[rt]=f[ls]+f[rs];
    
    }
    
    int query(int rt,int L,int R,int l,int r)
    
    {
    
        if(l<=L&&R<=r)
    
            return f[rt];
    
        int mid=(L+R)/2,ans=0;
    
        up(rt,L,mid,R);///去掉延迟
    
        if(l<=mid)///代表左孩子的区间中含有查询的区间
    
            ans+=query(ls,L,mid,l,r);
    
        if(r>=mid+1)///代表右孩子的区间中含有查询的区间
    
            ans+=query(rs,mid+1,R,l,r);
    
        return ans;
    
    }
    
    int main()
    
    {
    
        int n;
    
        scanf("%d",&n);
    
        rep(i,1,n)
    
        scanf("%d",&a[i]);
    
        build(1,1,n);
    
        int q;
    
        scanf("%d",&q);
    
        int l,r,v;
    
        while(q--)
    
        {
    
            int opt;
    
            scanf("%d",&opt);
    
            if(opt==1)
    
            {
    
                scanf("%d %d %d",&l,&r,&v);
    
                update(1,1,n,l,r,v);
    
            }
    
            else
    
            {
    
                scanf("%d %d",&l,&r);
    
                printf("%d
    ",query(1,1,n,l,r));
    
            }
    
        }
    
    }
  • 相关阅读:
    整除15问题
    软件工程基础Proposal提议
    对在大学阶段软件工程实践的一些想法
    运行web项目端口占用问题
    Day_1
    error C3615: constexpr 函数 "QAlgorithmsPrivate::qt_builtin_ctz" 不会生成常数表达式
    Qt应用程序的打包
    将html代码部署到阿里云服务器,并进行域名解析,以及在部署过程中遇到的问题和解决方法
    linux部署html代码到linux服务器,并进行域名解析
    运行sudo apt-get install nginx时报错有几个软件包无法下载,要不运行 apt-get update 或者加上 --fix-missing 的选项再试试?解决
  • 原文地址:https://www.cnblogs.com/lmhyhblog/p/10175781.html
Copyright © 2011-2022 走看看