|算法讨论|线段树1(大白书版本) 学习笔记
2017-01-22 15:27
369 查看
常用方法:
1、点修改,区间查询
2、区间增加,区间查询
3、区间修改,区间查询
4、混合多种修改,区间查询
5、离散化操作
6、二维线段树
1、点修改,区间查询
2、区间增加,区间查询
3、区间修改,区间查询
4、混合多种修改,区间查询
(1) 修改+增加
https://www.luogu.org/problem/show?pid=3373
1、点修改,区间查询
2、区间增加,区间查询
3、区间修改,区间查询
4、混合多种修改,区间查询
5、离散化操作
6、二维线段树
1、点修改,区间查询
input: 10 6 5 4 8 9 7 2 4 1 5 7 1 1 10 0 8 6 1 1 10 1 2 5 0 4 -8 1 1 5 //n m //n个数,表示线段树节点的初始值 //m行,每行一个指令 //0 p v,修改p节点为v //1 x y,求x~y区间最小值
output: 1 2 4 -8
#include<cstdio> #include<algorithm> #include<cstring> #include<set> #include<string> #define ms(i,j) memset(i,j,sizeof i); using namespace std; const int MAXN = 1000 + 5; int n,m; int mino[MAXN];//最小值 int p,v; int update(int o, int l, int r)//更新a[p] = v; { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (l==r) mino[o] = v; else { if (p<=m) update(lc, l, m); else update(rc, m+1, r); mino[o] = min(mino[lc], mino[rc]); } } int x,y; int query(int o, int l ,int r)//查询x,y区间最小值 { int m = (l+r)/2; int lc = o*2, rc = o*2+1; int ans = 100000000; if (x<=l&&r<=y) return mino[o]; if (x<=m) ans = min(ans, query(lc, l, m)); if (m<y) ans = min(ans, query(rc, m+1, r)); return ans; } int main() { freopen("st.in", "r", stdin);freopen("st.out", "w", stdout); scanf("%d%d", &n ,&m); for (int i=1;i<=n;i++)//输入初值 { scanf("%d", &v); p = i; update(1,1,n);//利用修改值来赋值初值 } for (int i=1;i<=m;i++)//输入m个指令 { int type; scanf("%d", &type); if (type==1)//求x,y的最小值 { scanf("%d%d", &x, &y); printf("%d\n", query(1,1,n)); } else//修改a[p] = v; { scanf("%d%d", &p, &v); update(1,1,n); } } fclose(stdin);fclose(stdout); return 0; }
2、区间增加,区间查询
input: 10 10 5 4 8 9 7 2 4 1 5 7 1 1 10 2 1 10 3 1 10 0 6 7 10 2 2 8 3 2 5 0 4 6 -7 3 4 8 0 10 10 -17 2 7 10 //n m //n个数,表示线段树节点的初始值 //m行,每行一个指令 //0 x y v,把x~y增加v //1 x y,求x~y区间和 //2 x y,求x~y区间最小值 //3 x y,求x~y区间最大值
output: 52 1 9 1 9 14 -10
#include<cstdio> #include<algorithm> #include<cstring> #define ms(i,j) memset(i,j,sizeof i); using namespace std; const int MAXN = 1000 + 5; int n,m; int _max, _min, _sum; int maxv[MAXN], minv[MAXN], addv[MAXN], sumv[MAXN]; int mt(int o, int l, int r)//更新结点信息 { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (r>l) { sumv[o] = sumv[lc]+sumv[rc]; minv[o] = min(minv[lc], minv[rc]); maxv[o] = max(maxv[lc], maxv[rc]); } minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += (r-l+1)*addv[o]; } int x,y,v; int update(int o, int l, int r)//使x,y区间都加上v { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (x<=l&&r<=y) { addv[o] = v; } else { if (x<=m) update(lc, l, m); if (m<y) update(rc, m+1, r); } mt(o, l ,r); } int query(int o, int l, int r, int add)//查询x,区间信息 { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (x<=l&&r<=y) { _sum += sumv[o] + add * (r-l+1); _min = min(_min, minv[o]+add); _max = max(_max, maxv[o]+add); }else { if (x<=m) query(lc, l, m,add+addv[o]); if (m<y) query(rc, m+1, r,add+addv[o]); } } int main() { freopen("st.in", "r", stdin);freopen("st.out", "w", stdout); scanf("%d%d", &n, &m); for (int i=1;i<=n;i++)//输入初值 { scanf("%d", &v); x = y = i; update(1,1,n); } for (int i=1;i<=m;i++)//输入指令 { int type; scanf("%d", &type); if (type==0) { scanf("%d%d%d", &x, &y, &v); update(1,1,n); } else { scanf("%d%d", &x, &y); _sum=_max=0; _min=100000000; query(1,1,n,0); switch(type) { case 1: printf("%d\n", _sum); break; case 2: printf("%d\n", _min); break; case 3: printf("%d\n", _max); break; } } } fclose(stdin);fclose(stdout); return 0; }
3、区间修改,区间查询
input: 10 10 5 4 8 9 7 2 4 1 5 7 1 1 10 2 1 10 3 1 10 0 6 7 10 2 2 8 3 2 5 0 4 6 2 3 4 8 0 10 10 17 2 7 10 //n m //n个数,表示线段树节点的初始值 //m行,每行一个指令 //0 x y v,把x~y修改为v(v>=0) //1 x y,求x~y区间和 //2 x y,求x~y区间最小值 //3 x y,求x~y区间最大值
output: 52 1 9 1 9 10 1
#include<cstdio> #include<algorithm> #include<cstring> #define ms(i,j) memset(i,j,sizeof i); using namespace std; const int MAXN = 1000 + 5; int _max, _min, _sum; int setv[MAXN], minv[MAXN], maxv[MAXN], sumv[MAXN]; int n,m; int pushdown(int o)//标记下传 { int lc = o*2, rc = o*2+1; if (setv[o]>=0) { setv[lc] = setv[rc] = setv[o]; setv[o] = -1; } } int mt(int o, int l, int r)//更新结点信息 { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (r>l) { sumv[o] = sumv[lc] + sumv[rc]; minv[o] = min(minv[lc], minv[rc]); maxv[o] = max(maxv[lc], maxv[rc]); } if (setv[o]>=0) {minv[o]=maxv[o]=setv[o]; sumv[o] = setv[o]*(r-l+1);} } int x,y,v; int update(int o, int l, int r)//修改x,y区间值为v { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (x<=l&&r<=y) { setv[o] = v; } else { pushdown(o); if (x<=m) update(lc, l, m); else mt(lc, l, m); if (m<y) update(rc,m+1,r); else mt(rc, m+1,r); } mt(o,l,r); } int query(int o, int l, int r)//查询x,y区间信息 { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (setv[o]>=0) { _sum += setv[o] * (min(r,y)-max(l,x)+1); _min = min(_min, setv[o]); _max = max(_max, setv[o]); } else if (x<=l&&r<=y) { _sum += sumv[o]; _min = min(_min, minv[o]); _max = max(_max, maxv[o]); } else { if (x<=m) query(lc, l, m); if (m<y) query(rc,m+1,r); } } int main() { freopen("st.in", "r", stdin);freopen("st.out", "w", stdout); scanf("%d%d", &n, &m); ms(setv,-1);//负数说明没有set标记 for (int i=1;i<=n;i++)//输入初值 { scanf("%d", &v); x = y = i; update(1,1,n); } for (int i=1;i<=m;i++)//输入指令 { int type; scanf("%d", &type); if (type==0) { scanf("%d%d%d", &x, &y, &v); update(1,1,n); } else { scanf("%d%d", &x, &y); _sum=0;_max=-100000000; _min=100000000; query(1,1,n); switch(type) { case 1: printf("%d\n", _sum); break; case 2: printf("%d\n", _min); break; case 3: printf("%d\n", _max); break; } } } fclose(stdin);fclose(stdout); return 0; }
4、混合多种修改,区间查询
(1) 修改+增加
input: 10 10 1 2 3 4 5 6 7 8 9 10 2 1 10 0 2 4 5 2 1 6 1 2 7 2 2 3 9 1 1 10 3 2 8 9 0 2 6 2 2 3 7 2 10 10 //n,m //n个数,表示线段树初值 //m行,每行一个指令 //0 x y v x,y区间值全部修改为v(v>=0) //1 x y v x,y区间值全部增加v //2 x y 输出x,y区间最小值、最大值、区间和
output: min e300 :1 max:10 sum:55 min:1 max:6 sum:27 min:7 max:9 sum:55 min:11 max:12 sum:23 min:2 max:12 sum:20 min:13 max:13 sum:13
#include<cstdio> #include<algorithm> #include<cstring> #define ms(i,j) memset(i,j,sizeof i); using namespace std; const int MAXN = 1000 + 5; int n,m; int _max, _min, _sum; int maxv[MAXN], minv[MAXN], sumv[MAXN], addv[MAXN], setv[MAXN]; int mt(int o, int l, int r) { int m = (l+r)/2; int lc = o*2, rc = o*2+1; sumv[o] = minv[o] = maxv[o] = 0; if (setv[o]>=0) { sumv[o] = setv[o]*(r-l+1); minv[o] = maxv[o] = setv[o]; } else if (r>l) { sumv[o] = sumv[lc]+sumv[rc]; minv[o] = min(minv[lc], minv[rc]); maxv[o] = max(maxv[lc], maxv[rc]); } if (addv[o]!=0) {sumv[o] += addv[o] * (r-l+1); minv[o] += addv[o]; maxv[o] += addv[o];} } int pushdown(int o) { int lc = o*2, rc = o*2+1; if (setv[o]>=0) { setv[lc] = setv[rc] = setv[o]; addv[lc] = addv[rc] = 0;//传下去一定要删除add标记 setv[o] = -1; } if (addv[o]!=0) { addv[lc] += addv[o]; addv[rc] += addv[o]; addv[o] = 0; } } int x,y,v; int update_add(int o, int l, int r) { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (x<=l&&r<=y) addv[o] += v; else { if (x<=m) update_add(lc, l, m); if (m<y) update_add(rc, m+1, r); } mt(o,l,r); } int update_set(int o, int l, int r) { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (x<=l&&r<=y) { setv[o] = v; addv[o] = 0; } else { pushdown(o); if (x<=m) update_set(lc, l, m); else mt(lc, l, m); if (m<y) update_set(rc, m+1, r); else mt(rc, m+1, r); } mt(o,l,r); } int query(int o, int l, int r, int add) { int m = (l+r)/2; int lc = o*2, rc = o*2+1; if (setv[o]>=0) { _sum += (setv[o]+add+addv[o])*(min(y,r)-max(x,l)+1); _min = min(_min, setv[o]+add+addv[o]); _max = max(_max, setv[o]+add+addv[o]); } else if (x<=l&&r<=y) { _sum += sumv[o]+add*(r-l+1); _min = min(_min, minv[o]+add); _max = max(_max, maxv[o]+add); } else { if (x<=m) query(lc, l, m,add+addv[o]); if (m<y) query(rc, m+1, r,add+addv[o]); } } int main() { freopen("st.in", "r", stdin);freopen("st.out", "w", stdout); scanf("%d%d", &n, &m);ms(setv, -1); for (int i=1;i<=n;i++) { scanf("%d", &v); x = y = i; update_set(1,1,n); } for (int i=1;i<=m;i++) { int type; scanf("%d", &type); if (type==0) { scanf("%d%d%d", &x, &y, &v); update_set(1,1,n); } else if (type==1) { scanf("%d%d%d", &x, &y, &v); update_add(1,1,n); } else { scanf("%d%d", &x, &y); _sum=_max=0; _min=100000000; query(1,1,n,0); printf("min:%d max:%d sum:%d\n", _min, _max, _sum); } } fclose(stdin);fclose(stdout); return 0; }(2)增加+乘法
https://www.luogu.org/problem/show?pid=3373
相关文章推荐
- |算法讨论|线段树2 学习笔记
- |算法讨论|AC自动机 学习笔记
- |算法讨论|平衡树 学习笔记
- |算法讨论|树链剖分 学习笔记
- |算法讨论|强连通分量Tarjan 学习笔记
- 数据结构&算法学习笔记: 线段树
- |算法讨论|2-SAT 学习笔记
- |算法讨论|Hash表 学习笔记
- |算法讨论|KMP 学习笔记
- |算法讨论|并查集 学习笔记
- |算法讨论|可并堆 学习笔记
- |算法讨论|无向图割点和桥 学习笔记
- 【算法学习笔记】81.动态规划 分类讨论 SJTU OJ 1075 括号匹配升级
- |算法讨论|差分约束 学习笔记
- |算法讨论|单调队列 学习笔记
- |算法讨论|树状数组 学习笔记
- 学习笔记——线段树算法学习
- |算法讨论|后缀数组 学习笔记
- |算法讨论|LCA 学习笔记
- |算法讨论|状压DP/位运算 学习笔记