线段树_模版
线段树
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为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 }
区间最大值
描述:第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 }
指针版:
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 }
为什么写指针?
因为不容易MLE(不存在访问无效内存),但是写起来会需要小心一点