线段树入坑到放弃

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为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\n",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\n",search(1,x,y));
71         }
72     }
73     return 0;
74 }
View Code

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

猜你喜欢

转载自www.cnblogs.com/Aamir-Dan/p/11345885.html
今日推荐