线段树_模版

线段树

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

区间修改与求和

给出数的个数n以及操作数q:
对于q:
1 x y z 令区间[x, y]增加z
2 x y 求区间和

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define LL long long
 5 using namespace std;
 6 const int maxn=1e5+10;
 7 int n, q;
 8 LL a[maxn];
 9 LL sum[maxn<<2], li[maxn<<2];
10 
11 void _push_up(int rt) {
12     sum[rt]=sum[rt<<1]+sum[(rt<<1)|1];
13 }//向上更新,求区间和
14 
15 void _setup(int l, int r, int rt) {
16     li[rt]=0;
17     if(l==r){
18         sum[rt]=a[l];
19         return ;
20     }
21     int mid=(l+r)>>1;
22     _setup(l, mid, rt<<1);
23     _setup(mid+1, r, (rt<<1)|1);
24     _push_up(rt);
25 }//建树
26 
27 void _push_down(int rt, int m) {
28     if(li[rt]) {
29         li[rt<<1]+=li[rt];
30         li[(rt<<1)|1]+=li[rt];
31         sum[rt<<1]+=li[rt]*(m-(m>>1));
32         sum[(rt<<1)|1]+=li[rt]*(m>>1);
33         li[rt]=0;
34     }
35 }//将li标记向下更新,传递给小区间
36 
37 void _add(int x, int y, LL z, int l ,int r, int rt) {
38     if(x<=l && y>=r) {
39         li[rt]+=z;
40         sum[rt]+=z*(r-l+1);
41         return ;
42     }
43     _push_down(rt, r-l+1); //边算边更新,节省复杂度
44     int mid=(l+r)>>1;
45     if(x<=mid) _add(x, y, z, l, mid, rt<<1);
46     if(y>mid) _add(x, y, z, mid+1, r, (rt<<1)|1);
47     _push_up(rt);
48 }
49 
50 LL _query(int x, int y, int l, int r, int rt) {
51     if(x<=l && y>=r) {
52         return sum[rt];
53     }
54     _push_down(rt, r-l+1); //求和时再顺便更新一次
55     int mid=(l+r)>>1;
56     LL ans=0;
57     if(x<=mid) ans+=_query(x, y, l, mid, rt<<1);
58     if(y>mid) ans+=_query(x, y, mid+1, r, (rt<<1)|1); //'夹'区间
59     return ans;
60 }
61 
62 int main() {
63     scanf("%d%d", &n, &q);
64     for(int i=1; i<=n; i++) {
65         scanf("%lld", a+i);
66     }
67     _setup(1, n, 1);
68     for(int i=0; i<q; i++) {
69         int ls;
70         scanf("%d", &ls);
71         if(ls==1) {
72             int x, y;
73             LL z;
74             scanf("%d%d%lld", &x, &y, &z);
75             _add(x, y, z, 1, n, 1);
76         } else if(ls==2) {
77             int x, y;
78             scanf("%d%d", &x, &y);
79             printf("%lld\n", _query(x, y, 1, n ,1));
80         }
81     }
82     return 0;
83 }
View Code

区间最大值

描述:第1行给出数的个数n,操作数m
第2行为n个数
第3到m+3-1行有两个数a,b,求区间[a,b]中的最大数

样例:

Input

8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8

Output

9
9
7
7
9
8
7
9

代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define CL(X,N) memset(X, N, sizeof(X))
 5 #define LL long long
 6 #define ls root<<1
 7 #define rs root<<1|1
 8 using namespace std;
 9 const int maxn=1e5+10;
10 int n, m;
11 int a[maxn], t[maxn<<2];
12 
13 void _pushup(int root) {
14     t[root]=max(t[ls], t[rs]);
15 }
16 
17 void _set(int l, int r, int root) {
18     if(l==r) {
19         t[root]=a[l];
20         return ;
21     }
22     int mid=(l+r)>>1;
23     _set(l, mid, ls);
24     _set(mid+1, r, rs);
25     _pushup(root);
26 }
27 
28 int _query(int l, int r, int root, int a, int b) {
29     if(a<=l && b>=r) {
30         return t[root];
31     }
32     int mid=(l+r)>>1, ans=0;
33     if(a<=mid) ans=_query(l, mid, ls, a, b);
34     if(b>mid) ans=max(ans, _query(mid+1, r, rs, a, b));
35     return ans;
36 }
37 
38 int main() {
39     scanf("%d%d", &n, &m);
40     for(int i=1; i<=n; i++) {
41         scanf("%d", a+i);
42     }
43     _set(1, n, 1);
44     for(int i=0; i<m; i++) {
45         int a, b;
46         scanf("%d%d", &a, &b);
47         printf("%d\n", _query(1, n, 1, a, b));
48     }
49     return 0;
50 }
View Code

指针版:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #define CL(X,N) memset(X, N, sizeof(X))
 5 #define LL long long
 6 using namespace std;
 7 const int maxn=1e5+10;
 8 int n, m;
 9 int a[maxn];
10 struct Bit {
11     int l, r, data;
12     Bit *ls, *rs;
13     Bit(){
14         l=0, r=0, data=0;
15         ls=rs=NULL;
16     }
17 }*t;
18 
19 #define lc root->ls
20 #define rc root->rs
21 void _pushup(Bit *root) {
22     root->data=max(lc->data, rc->data);
23 }
24 
25 void _set(int l, int r, Bit *root) {
26     root->l=l;
27     root->r=r;
28     if(l==r) {
29         root->data=a[l];
30         return ;
31     }
32     if(!lc) lc=new Bit;
33     if(!rc) rc=new Bit;
34     int mid=(l+r)>>1;
35     _set(l, mid, lc);
36     _set(mid+1, r, rc);
37     _pushup(root);
38 }
39 
40 int _query(Bit *root, int a, int b) {
41     if(!root) return 0;
42     int l=root->l, r=root->r;
43     if(a<=l && b>=r) {
44         return root->data;
45     }
46     int mid=(l+r)>>1, ans=0;
47     if(a<=mid) ans=_query(lc, a, b);
48     if(b>mid) ans=max(ans, _query(rc, a, b));
49     return ans;
50 }
51 
52 int main() {
53     t=new Bit;
54     scanf("%d%d", &n, &m);
55     for(int i=1; i<=n; i++) {
56         scanf("%d", a+i);
57     }
58     _set(1, n, t);
59     for(int i=0; i<m; i++) {
60         int x, y;
61         scanf("%d%d", &x, &y);
62         printf("%d\n", _query(t, x, y));
63     }
64     return 0;
65 }
View Code

 

为什么写指针?

因为不容易MLE(不存在访问无效内存),但是写起来会需要小心一点

 

posted @ 2018-06-02 20:41  Nomaldisk  阅读(170)  评论(0编辑  收藏  举报