• [Data]Segment Tree


    Segment Tree——线段树

    线段树是一种二叉搜索树,它将一个单元划分为几个单元,实际应用是应开4*N空间防止越界。

    线段树中的节点可以用来存区间和,区间最小值,最大值都可以。

    图中的栗子就是对于数组[2,5,1,4,9,3]中维护区间最小值。

    当线段树为叶子节点时,它其实就是原数组中的一个元素而已。

    为非叶子节点,代表它的所有子树节点所包含的区间的最小值。

    由于线段树是平均分割左右子树的,所以它是完全二叉树,对于一棵有n个叶子节点的线段树,

    它一定有n-1个非叶子节点,所以它的总节点数为2*n-1。


     

    build——建树

    我们用一个数组来存线段树节点,在当前节点,它的左右儿子节点分别为2*n与2*n+1,我们可以从根节点开始,平分左右区间,

    递归创建线段树。

    void build(int o,int l,int r)
    {
        int mid;
        if(l==r)
        {
            tree[o]=a[l];
            return;
        }
        else
        {
            mid=(l+r)/2;
            dfs(2*o,l,mid);
            dfs(2*o+1,mid+1,r);
            tree[o]=tree[o*2]+tree[o*2+1];
        }
    }
    build

    query——查询

    当我们要查询一个区间的和时(栗子),我们可以把这个区间分散成好几个区间,在当前区间,如果这个区间被要查询的区间完全包含,那么可以直接返回。

    如果没有,我们就需判断,它的左右区间与查询区间是否有交集,若有,便递归进入。

    void query(int root,int l,int r)
    {
        if(l>r) return;
        if(l>end||r<start)
        return;
        if(start<=l&&end>=r)
        {
             ans+=tree[root];
             return;
        }
        push(root,l,r);
        int mid=(l+r)/2;
        if(start<=mid) query(root*2,l,mid);
        if(end>mid) query(root*2+1,mid+1,r);
    }
    qurey

    单点更新

    从根节点更新,判断更新节点所在位置,递归进入左右子节点,但因为更新时它的父节点会产生影响,所以在递归到叶子节点后需回溯更新它的父节点。

    区间更新

    我们推出了一个叫做“延迟标记"的新产品 ,表明这个点是否需要进行某种修改,若不为0,则需进行修改并下传标记,自己消除标记。

    以下是区间每个数加上x。

    void push(int root,int l,int r)
    {
        int mid=(l+r)/2;
        if(add[root]!=0)
        {
            add[root*2]+=add[root];
            add[root*2+1]+=add[root];
            tree[root*2]+=add[root]*(mid-l+1);
            tree[root*2+1]+=add[root]*(r-mid);
            add[root]=0;
        }
    }
    void update(int root,int l,int r)
    {
        if(l>end||r<start)
        return;
        if(l>=start&&r<=end)
        {
            add[root]+=ad;
            tree[root]+=ad*(r-l+1);
            return;
        }
        push(root,l,r);
        int mid=(l+r)/2;
        update(root*2,l,mid);
        update(root*2+1,mid+1,r);
        tree[root]=tree[root*2]+tree[root*2+1];
    }
    区间更新

    注意!若操作中有要求区间乘某个数,那么注意要先乘后加,因为你先加后乘会使实际数值偏大!

     1 void push(int root,int l,int r)
     2 {
     3     int mid=(l+r)/2;
     4     if(aadd[root]!=1)
     5     {
     6         add[root*2]=aadd[root]*add[root*2]%p;
     7         add[root*2+1]=aadd[root]*add[root*2+1]%p;
     8         aadd[root*2]=aadd[root]*aadd[root*2]%p;
     9         aadd[root*2+1]=aadd[root]*aadd[root*2+1]%p;
    10         tree[root*2]=aadd[root]*tree[root*2]%p;
    11         tree[root*2+1]=aadd[root]*tree[root*2+1]%p;
    12         aadd[root]=1;
    13     }
    14     if(add[root]!=0)
    15     {
    16         add[root*2]+=add[root];
    17         add[root*2]%=p;
    18         add[root*2+1]+=add[root];
    19         add[root*2+1]%=p;
    20         tree[root*2]+=add[root]*(mid-l+1);
    21         tree[root*2]%=p;
    22         tree[root*2+1]+=add[root]*(r-mid);
    23         tree[root*2+1]%=p;
    24         add[root]=0;
    25     }
    26 }
    27 long long query(int root,int l,int r)
    28 {
    29     if(l>end||r<start)
    30     return 0;
    31     if(start<=l&&end>=r)
    32     {
    33          return tree[root];
    34     }
    35     push(root,l,r);
    36     int mid=(l+r)/2;
    37     return (query(root*2,l,mid)+query(root*2+1,mid+1,r))%p;
    38 }
    先乘后加!! 

    总代码

    luogu 3372

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    long long int a[400000]={0},tree[400000]={0},ql,qr,add[400000]={0},end,start,ad,ans=0;
    void dfs(int o,int l,int r)
    {
        int mid;
        if(l==r)
        {
            tree[o]=a[l];
            return;
        }
        else
        {
            mid=(l+r)/2;
            dfs(2*o,l,mid);
            dfs(2*o+1,mid+1,r);
            tree[o]=tree[o*2]+tree[o*2+1];
        }
    }
    void push(int root,int l,int r)
    {
        int mid=(l+r)/2;
        if(add[root]!=0)
        {
            add[root*2]+=add[root];
            add[root*2+1]+=add[root];
            tree[root*2]+=add[root]*(mid-l+1);
            tree[root*2+1]+=add[root]*(r-mid);
            add[root]=0;
        }
    }
    void query(int root,int l,int r)
    {
        if(l>r) return;
        if(l>end||r<start)
        return;
        if(start<=l&&end>=r)
        {
             ans+=tree[root];
             return;
        }
        push(root,l,r);
        int mid=(l+r)/2;
        if(start<=mid) query(root*2,l,mid);
        if(end>mid) query(root*2+1,mid+1,r);
    }
    void update(int root,int l,int r)
    {
        if(l>end||r<start)
        return;
        if(l>=start&&r<=end)
        {
            add[root]+=ad;
            tree[root]+=ad*(r-l+1);
            return;
        }
        push(root,l,r);
        int mid=(l+r)/2;
        update(root*2,l,mid);
        update(root*2+1,mid+1,r);
        tree[root]=tree[root*2]+tree[root*2+1];
    }
    int main()
    {
        int n,i,c,s;
        scanf("%d%d",&n,&c);
        for(i=1;i<=n;i++)
        scanf("%lld",&a[i]);
            dfs(1,1,n);
            for(i=1;i<=c;i++)
            {
                scanf("%d%lld%lld",&s,&start,&end);
                if(s==1)
                {
                    scanf("%lld",&ad);
                    update(1,1,n);
                }
                else
                {
                    ans=0;
                    query(1,1,n);
                    //for (int j=1;j<=n*2-1;j++){
                    //    printf("%d %d
    ",j,tree[j]);
                //    }
                    printf("%lld
    ",ans);
                }
        }
        return 0;}
    线段树1

    luogu 3373

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    long long int a[400000]={0},tree[400000]={0},ql,qr,add[400000]={0},end,start,ad,
    aadd[400000],k,p;
    void dfs(int o,int l,int r)
    {
        aadd[o]=1;
        add[o]=0;
        int mid;
        if(l==r)
        {
            tree[o]=a[l]%p;
        }
        else
        {
            mid=(l+r)/2;
            dfs(2*o,l,mid);
            dfs(2*o+1,mid+1,r);
            tree[o]=(tree[o*2]+tree[o*2+1])%p;
        }
    }
    void push(int root,int l,int r)
    {
        int mid=(l+r)/2;
        if(aadd[root]!=1)
        {
            add[root*2]=aadd[root]*add[root*2]%p;
            add[root*2+1]=aadd[root]*add[root*2+1]%p;
            aadd[root*2]=aadd[root]*aadd[root*2]%p;
            aadd[root*2+1]=aadd[root]*aadd[root*2+1]%p;
            tree[root*2]=aadd[root]*tree[root*2]%p;
            tree[root*2+1]=aadd[root]*tree[root*2+1]%p;
            aadd[root]=1;
        }
        if(add[root]!=0)
        {
            add[root*2]+=add[root];
            add[root*2]%=p;
            add[root*2+1]+=add[root];
            add[root*2+1]%=p;
            tree[root*2]+=add[root]*(mid-l+1);
            tree[root*2]%=p;
            tree[root*2+1]+=add[root]*(r-mid);
            tree[root*2+1]%=p;
            add[root]=0;
        }
    }
    long long query(int root,int l,int r)
    {
        if(l>end||r<start)
        return 0;
        if(start<=l&&end>=r)
        {
             return tree[root];
        }
        push(root,l,r);
        int mid=(l+r)/2;
        return (query(root*2,l,mid)+query(root*2+1,mid+1,r))%p;
    }
    void update(int root,int l,int r,int ad)
    {
        if(l>end||r<start)
        return;
        if(l>=start&&r<=end)
        {
            add[root]+=ad;
            add[root]%=p;
            tree[root]+=ad*(r-l+1);
            tree[root]%=p;
            return;
        }
        push(root,l,r);
        int mid=(l+r)/2;
        update(root*2,l,mid,ad);
        update(root*2+1,mid+1,r,ad);
        tree[root]=(tree[root*2]+tree[root*2+1])%p;
    }
    void cheng(int root,int l,int r,int k)
    {
    
        if(l>end||r<start)
        return;
        if(l>=start&&r<=end)
        {
            add[root]*=k;
            add[root]%=p;
            aadd[root]*=k;
            aadd[root]%=p;
            tree[root]*=k;
            tree[root]=tree[root]%p;
            return;
        }
       push(root,l,r);
        int mid=(l+r)/2;
        cheng(root*2,l,mid,k);
        cheng(root*2+1,mid+1,r,k);
        tree[root]=(tree[root*2]+tree[root*2+1])%p;
    }
    int main()
    {
        long long int n,i,c,s;
        scanf("%lld%lld%lld",&n,&c,&p);
        for(i=1;i<=n;i++)
        scanf("%lld",&a[i]);
            dfs(1,1,n);
            for(i=1;i<=n;i++)
            aadd[i]=1;
            for(i=1;i<=c;i++)
            {
                scanf("%lld",&s);
                if(s==1)
                {
                    scanf("%lld%lld%lld",&start,&end,&k);
                    cheng(1,1,n,k);
                }
                if(s==2)
                {
                    scanf("%lld%lld%lld",&start,&end,&ad);
                        update(1,1,n,ad);
                }
                else if(s==3)
                {
                    scanf("%lld%lld",&start,&end);
                    printf("%lld
    ",query(1,1,n)%p);
                }
           }
        return 0;
    }
    线段树2

     

  • 相关阅读:
    跨域
    reactV16理解
    css动画总结
    h5与app交互
    跨域
    ant-design如果按需加载组件
    移动端300ms延迟原理,穿透、遮罩层滑动导致下面滑动总结
    监听数组的变化
    使用VS Code调试Node.js
    React-typescript-antd 常见问题
  • 原文地址:https://www.cnblogs.com/Fish-/p/8179192.html
Copyright © 2020-2023  润新知