• 线段树入坑到放弃


    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
    使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
    一.建树:
    例如:一个长度为4的线段,我们可以表示成这样:

    如果你要表示线段的和,那么最上面的根节点的权值表示的是这个线段1~4的和。根的两个儿子分别表示这个线段中1~2的和,与3~4的和。以此类推。
    然后我们还可以的到一个性质:节点i的权值=她的左儿子权值+她的右儿子权值。因为1~4的和就是等于1~2的和+3~4的和。
    根据这个思路,我们就可以建树了,设一个结构体tree,tree[i].l和tree[i].r分别表示这个点代表的线段的左右下标,tree[i].sum表示这个节点表示的线段和。
    我们知道,一颗二叉树,她的左儿子和右儿子编号分别是她*2和她*2+1,得到式子:tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    建树代码:

     1 inline void build(int i,int l,int r){//递归建树
     2     tree[i].l=l;tree[i].r=r;
     3     if(l==r){//如果这个节点是叶子节点
     4         tree[i].sum=input[l];
     5         return ;
     6     }
     7     int mid=(l+r)>>1;
     8     build(i*2,l,mid);//分别构造左子树和右子树
     9     build(i*2+1,mid+1,r);
    10     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    11     return ;
    12 }

    二.修改:

    单点修改:
    从根节点开始,在区间的dis位上+/-/*/除k,原路返回时将左右节点的值加入父节点完成修改;
    代码(加法):

     1 inline void add(int i,int dis,int k){
     2     if(tree[i].l==tree[i].r){//此时说明找到了dis位
     3         tree[i].sum+=k;
     4         return ;
     5     }
     6     if(dis<=tree[i*2].r)  add(i*2,dis,k);//在哪往哪跑
     7     else  add(i*2+1,dis,k);
     8     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;//返回更新
     9     return ;
    10 }

    区间修改:
    此时需要记录一个“懒标记”lazytage,来记录这个区间,以便在查询需要的时候可以传递下去,减少时间
    区间修改的时候,我们按照如下原则:
    1、如果当前区间被完全覆盖在目标区间里,讲这个区间的sum+k*(tree[i].r-tree[i].l+1)
    2、如果没有完全覆盖,则先下传懒标记;
    3、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子;
    4、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子。

    push_down代码:

     1 void push_down(int i)
     2 {
     3     if(tree[i].lz!=0)
     4     {
     5         tree[i*2].lz+=tree[i].lz;//左右儿子分别加上父亲的lz
     6         tree[i*2+1].lz+=tree[i].lz;
     7         init mid=(tree[i].l+tree[i].r)/2;
     8         tree[i*2].data+=tree[i].lz*(mid-tree[i*2].l+1);//左右分别求和加起来
     9         tree[i*2+1].data+=tree[i].lz*(tree[i*2+1].r-mid);
    10         tree[i].lz=0;//父亲lz归零
    11     }
    12     return ;
    13 }

    区间修改代码:

     1 void add(int i,int l,int r,int k)
     2 {
     3     if(tree[i].r<=r && tree[i].l>=l)//如果当前区间被完全覆盖在目标区间里,将这个区间的sum+k*(tree[i].r-tree[i].l+1)
     4     {
     5         tree[i].sum+=k*(tree[i].r-tree[i].l+1);
     6         tree[i].lz+=k;//记录lazytage
     7         return ;
     8     }
     9     push_down(i);//向下传递
    10     if(tree[i*2].r>=l)
    11         add(i*2,l,r,k);
    12     if(tree[i*2+1].l<=r)
    13         add(i*2+1,l,r,k);
    14     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    15     return ;
    16 }

    三.查询:
    单点查询:

     1 void search(int i,int dis){
     2     ans+=tree[i].num;//一路加起来
     3     if(tree[i].l==tree[i].r)
     4         return ;
     5     push_down(i);//若无区间修改这步可删
     6     if(dis<=tree[i*2].r)
     7         search(i*2,dis);
     8     if(dis>=tree[i*2+1].l)
     9         search(i*2+1,dis);
    10 }

    区间查询:
    线段树的查询方法:
    1、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值;
    2、如果没有完全覆盖,则先下传懒标记;
    3、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子;
    4、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子。
    区间查询代码:

    inline int search(int i,int l,int r){
        if(tree[i].l>=l && tree[i].r<=r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
             return tree[i].sum;
        if(tree[i].r<l || tree[i].l>r)  return 0;//如果这个区间和目标区间毫不相干,返回0
        push_down(i);//若无区间修改这步可删
        int s=0;
        if(tree[i*2].r>=l)  s+=search(i*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
        if(tree[i*2+1].l<=r)  s+=search(i*2+1,l,r);//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
        return s;
    }

    四.乘法/除法/根号线段树(以后补)

    五.模拟题及代码:

    【模板】树状数组 1(单点修改及区间查询)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=5e7+20;
     4 struct node
     5 {
     6     int r,l,sum;
     7 }tree[maxn];
     8 int input[maxn];
     9 void build(int i,int l,int r)
    10 {
    11     tree[i].l=l;tree[i].r=r;
    12     if(l==r){
    13         tree[i].sum=input[l];return ;
    14     }
    15     int mid=(l+r)>>1;
    16     build(i*2,l,mid);
    17     build(i*2+1,mid+1,r);
    18     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    19 }
    20 int search(int i,int l,int r)
    21 {
    22     if(tree[i].l>=l&&tree[i].r<=r)return tree[i].sum;
    23     if(tree[i].r<l||tree[i].l>r)return 0;
    24     int s=0;
    25     if(tree[i*2].r>=l)s+=search(i*2,l,r);
    26     if(tree[i*2+1].l<=r)s+=search(i*2+1,l,r);
    27     return s;
    28 }
    29 void add(int i,int dis,int k)
    30 {
    31     if(tree[i].l==tree[i].r){
    32         tree[i].sum+=k;
    33         return ;
    34     }
    35     if(dis<=tree[i*2].r)add(i*2,dis,k);
    36     else add(i*2+1,dis,k);
    37     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    38     return ;
    39 }
    40 int n,m,book,x,y,k;
    41 int main()
    42 {
    43     scanf("%d%d",&n,&m);
    44     for(int i=1;i<=n;i++){
    45         scanf("%d",&input[i]);
    46     }
    47     build(1,1,n);
    48     for(int i=1;i<=m;i++){
    49         scanf("%d",&book);
    50         if(book==1){
    51             scanf("%d%d",&x,&k);
    52             add(1,x,k);
    53         }
    54         else if(book==2){
    55             scanf("%d%d",&x,&y);
    56             printf("%d
    ",search(1,x,y));
    57         }
    58     }
    59     return 0;
    60 }
    View Code

    【模板】线段树 1(区间加法及查询)

     1 #include<bits/stdc++.h>
     2 #define LL long long
     3 using namespace std;
     4 const int maxn=1e7+20;
     5 struct node
     6 {
     7     LL r,l,sum,lz;
     8 }tree[maxn];
     9 int input[maxn];
    10 void build(LL i,LL l,LL r)
    11 {
    12     tree[i].l=l,tree[i].r=r;
    13     if(l==r){
    14         tree[i].sum=input[l];return ;
    15     }
    16     LL mid=(l+r)>>1;
    17     build(i*2,l,mid);
    18     build(i*2+1,mid+1,r);
    19     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    20 }
    21 void push_down(LL i)
    22 {
    23     if(tree[i].lz){
    24         tree[i*2].lz+=tree[i].lz,tree[i*2+1].lz+=tree[i].lz;
    25         LL mid=(tree[i].l+tree[i].r)/2;
    26         tree[i*2].sum+=tree[i].lz*(mid-tree[i*2].l+1);
    27         tree[i*2+1].sum+=tree[i].lz*(tree[i*2+1].r-mid);
    28         tree[i].lz=0;
    29     }
    30     return ;
    31 }
    32 LL search(LL i,LL l,LL r)
    33 {
    34     if(tree[i].l>=l&&tree[i].r<=r)return tree[i].sum;
    35     push_down(i);
    36     LL s=0;
    37     if(tree[i*2].r>=l)s+=search(i*2,l,r);
    38     if(tree[i*2+1].l<=r)s+=search(i*2+1,l,r);
    39     return s;
    40 }
    41 void add(LL i,LL l,LL r,LL k)
    42 {
    43     if(tree[i].l>=l&&tree[i].r<=r){
    44         tree[i].sum+=k*(tree[i].r-tree[i].l+1);
    45         tree[i].lz+=k;
    46         return ;
    47     }
    48     push_down(i);
    49     if(tree[i*2].r>=l)add(i*2,l,r,k);
    50     if(tree[i*2+1].l<=r) add(i*2+1,l,r,k);
    51     tree[i].sum=tree[i*2].sum+tree[i*2+1].sum;
    52     return ;
    53 }
    54 LL n,m,book,x,y,k;
    55 int main()
    56 {
    57     scanf("%lld%lld",&n,&m);
    58     for(int i=1;i<=n;i++){
    59         scanf("%lld",&input[i]);
    60     }
    61     build(1,1,n);
    62     for(int i=1;i<=m;i++){
    63         scanf("%lld",&book);
    64         if(book==1){
    65             scanf("%lld%lld%lld",&x,&y,&k);
    66             add(1,x,y,k);
    67         }
    68         else if(book==2){
    69             scanf("%lld%d",&x,&y);
    70             printf("%lld
    ",search(1,x,y));
    71         }
    72     }
    73     return 0;
    74 }
    View Code

    参考于:https://www.cnblogs.com/jason2003/p/9676729.html

  • 相关阅读:
    纯CSS实现气泡聊天框的方法
    css实现箭头矩形流程效果
    上传文件控件的样式美化
    Input placeholder 字体颜色更改
    如何为全局所有input组件添加边框发光效果
    JavaWeb:Filter(二)
    JavaWeb:Filter
    JavaWeb:EL & JSTL
    JavaWeb:标签(二)
    JavaWeb:简单的标签
  • 原文地址:https://www.cnblogs.com/Aamir-Dan/p/11345885.html
Copyright © 2020-2023  润新知