线段树小结

线段树

作用

适合具备结合律的运算(sum,xor,max,min……)

像归并排序一样,把所有数据反复切一半

那么叶子节点即为原数的自身区间    exam:[i,i]

按照BFS的顺序将这棵树编号(外层为层数,内层为同意层的序号)

最后一层节点可能不是连续的,直接空出来即可。

可以发现,每个非叶子节点的左儿子的编号是其两倍,右儿子的编号是其两倍加一

保存线段树的数组(struct)长度不能小于4*N

void build(int now,int l,int r)

  1. 更新l,r
  2. 判断是否为叶子节点(l==r)
  3. int mid=(l+r)>>2//卡常省时
  4. build左区间&&右区间
  5. 回溯后(不用返回值)将数据代入递推式

递推式:(以max为例)

dat[l,r]=max(dat[l,mid],dat[mid,r])

建立线段树以后,每个节点上的信息就为其对应区间的最大值

模板题

P3372 【模板】线段树 1

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #define LL long long
 4 using namespace std;
 5 struct Edge{
 6     LL l,r,sum,add;
 7     #define l(x) tree[x].l
 8     #define r(x) tree[x].r
 9     #define sum(x) tree[x].sum
10     #define add(x) tree[x].add
11 }tree[400010];
12 LL n,m; 
13 LL ans[100010];
14 void build(int l,int r,int p)
15 {
16     l(p)=l,r(p)=r;
17     if(l==r){sum(p)=ans[l];return;}
18     LL mid=(l+r)/2;
19     build(l,mid,p*2);
20     build(mid+1,r,p*2+1);
21     sum(p)=sum(p*2)+sum(p*2+1);
22 }
23 void spread(LL p)
24 {
25     if(add(p))
26     {
27         sum(p*2)+=add(p)*(r(p*2)-l(p*2)+1);
28         sum(p*2+1)+=add(p)*(r(p*2+1)-l(p*2+1)+1);
29         add(p*2)+=add(p);
30         add(p*2+1)+=add(p);
31         add(p)=0;
32     }
33 }
34 void change(LL l,LL r,LL p,LL k)
35 {
36     if(l<=l(p)&&r>=r(p)){add(p)+=k;sum(p)+=k*(r(p)-l(p)+1);return;}
37     LL mid=(l(p)+r(p))/2;
38     spread(p);
39     if(l<=mid) change(l,r,p*2,k);
40     if(r>=mid+1) change(l,r,p*2+1,k);
41     sum(p)=sum(p*2)+sum(p*2+1);
42 }
43 LL ask(LL l,LL r,LL p)
44 {
45     if(l<=l(p)&&r>=r(p)) return sum(p);
46     spread(p);
47     LL mid=(l(p)+r(p))/2,val=0;
48     if(l<=mid) val+=ask(l,r,p*2);
49     if(r>=mid+1) val+=ask(l,r,p*2+1);
50     return val;
51 }
52 int main()
53 {
54     cin>>n>>m;
55     for(int i=1;i<=n;i++) cin>>ans[i];
56     build(1,n,1);
57     int t,a,b,c;
58     while(m--)
59     {
60         cin>>t;
61         if(t==1){
62             cin>>a>>b>>c;
63             change(a,b,1,c);
64         }
65         if(t==2){
66             cin>>a>>b;
67             cout<<ask(a,b,1)<<endl;
68         }
69     }
70 }

例题2

P2357 守墓人

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #define LL long long
 4 using namespace std;
 5 struct Edge{
 6     LL l,r,sum,add;
 7     #define l(x) tree[x].l
 8     #define r(x) tree[x].r
 9     #define sum(x) tree[x].sum
10     #define add(x) tree[x].add
11 }tree[800010];
12 LL n,m; 
13 LL ans[200010];
14 void build(int l,int r,int p)
15 {
16     l(p)=l,r(p)=r;
17     if(l==r){sum(p)=ans[l];return;}
18     LL mid=(l+r)/2;
19     build(l,mid,p*2);
20     build(mid+1,r,p*2+1);
21     sum(p)=sum(p*2)+sum(p*2+1);
22 }
23 void spread(LL p)
24 {
25     if(add(p))
26     {
27         sum(p*2)+=add(p)*(r(p*2)-l(p*2)+1);
28         sum(p*2+1)+=add(p)*(r(p*2+1)-l(p*2+1)+1);
29         add(p*2)+=add(p);
30         add(p*2+1)+=add(p);
31         add(p)=0;
32     }
33 }
34 void change(LL l,LL r,LL p,LL k)
35 {
36     if(l<=l(p)&&r>=r(p)){add(p)+=k;sum(p)+=k*(r(p)-l(p)+1);return;}
37     LL mid=(l(p)+r(p))/2;
38     spread(p);
39     if(l<=mid) change(l,r,p*2,k);
40     if(r>=mid+1) change(l,r,p*2+1,k);
41     sum(p)=sum(p*2)+sum(p*2+1);
42 }
43 LL ask(LL l,LL r,LL p)
44 {
45     if(l<=l(p)&&r>=r(p)) return sum(p);
46     spread(p);
47     LL mid=(l(p)+r(p))/2,val=0;
48     if(l<=mid) val+=ask(l,r,p*2);
49     if(r>=mid+1) val+=ask(l,r,p*2+1);
50     return val;
51 }
52 int main()
53 {
54     cin>>n>>m;
55     for(int i=1;i<=n;i++) cin>>ans[i];
56     build(1,n,1);
57     int t,a,b,c;
58     while(m--)
59     {
60         cin>>t;
61         if(t==1){
62             cin>>a>>b>>c;
63             change(a,b,1,c);
64         }
65         if(t==2){
66             cin>>c;
67             change(1,1,1,c);
68         }
69         if(t==3){
70             cin>>c;
71             change(1,1,1,-c);
72         }
73         if(t==4){
74             cin>>a>>b;
75             cout<<ask(a,b,1)<<endl;
76         }
77         if(t==5){
78             cout<<ask(1,1,1)<<endl;
79         }
80     }
81 }

卡常

很明显,这玩意可以卡常

以模板题为例

把p*2换成p<<1

把p*2+1换成p<<1|1

把(l+r)/2换成l+r>>1

但是不能写p<<1|1,否则会使1+1先计算

 

 

 Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 struct Edge{
 7     LL l,r,sum,add;
 8     #define l(x) tree[x].l
 9     #define r(x) tree[x].r
10     #define sum(x) tree[x].sum
11     #define add(x) tree[x].add
12 }tree[400010];
13 LL n,m; 
14 LL ans[100010];
15 void build(LL l,LL r,LL p)
16 {
17     l(p)=l,r(p)=r;
18     if(l==r){sum(p)=ans[l];return;}
19     LL mid=(l+r)>>1;
20     build(l,mid,p<<1);
21     build(mid+1,r,p<<1|1);
22     sum(p)=sum(p<<1)+sum(p<<1|1);
23 }
24 void spread(LL p)
25 {
26     if(add(p))
27     {
28         sum(p<<1)+=add(p)*(r(p<<1)-l(p<<1)+1);
29         sum(p<<1|1)+=add(p)*(r(p<<1|1)-l(p<<1|1)+1);
30         add(p<<1)+=add(p);
31         add(p<<1|1)+=add(p);
32         add(p)=0;
33     }
34 }
35 void change(LL l,LL r,LL p,LL k)
36 {
37     if(l<=l(p)&&r>=r(p)){add(p)+=k;sum(p)+=k*(r(p)-l(p)+1);return;}
38     LL mid=l(p)+r(p)>>1;
39     spread(p);
40     if(l<=mid) change(l,r,p<<1,k);
41     if(r>mid) change(l,r,p<<1|1,k);
42     sum(p)=sum(p<<1)+sum(p<<1|1);
43 }
44 LL ask(LL l,LL r,LL p)
45 {
46     if(l<=l(p)&&r>=r(p)) return sum(p);
47     spread(p);
48     LL mid=l(p)+r(p)>>1,val=0;
49     if(l<=mid) val+=ask(l,r,p<<1);
50     if(r>mid) val+=ask(l,r,p<<1|1);
51     return val;
52 }
53 int main()
54 {
55     scanf("%lld%lld",&n,&m);
56     for(int i=1;i<=n;i++) scanf("%lld",ans+i); 
57     build(1,n,1);
58     LL t,a,b,c;
59     while(m--)
60     {
61         cin>>t;
62         if(t==1){
63             scanf("%lld%lld%lld",&a,&b,&c);
64             change(a,b,1,c);
65         }
66         if(t==2){
67             scanf("%lld%lld",&a,&b);
68             printf("%lld\n",ask(a,b,1));
69         }
70     }
71 }

小结

理论知识始终不实践==没有知识

纸上得来终觉浅,绝知此事要躬行。

posted @ 2019-08-05 18:10  Vanilla_chan  阅读(167)  评论(0编辑  收藏  举报