【暑假】[实用数据结构]动态范围查询问题
2015-08-06 18:48
363 查看
动态范围查询问题:
一、线段树+点修改
支持操作:
Update(x,v): 将Ax修改为v
Query(L,R) : 计算[L,R]内的最小值
联系题目:LA3938
链接:
二、[b]线段树+区间修改
[/b]
快速序列操作1:
支持操作:
Add(L,R,v):
Query(L,R):计算[L,R]内的最小值、最大值、区间和。
关于算法:在 一 的基础上增加了addv,这种为了避免复杂操作而打标记的方法与链表题UVa12657相通,新增maintain维护结点信息。需要注意的是Query的新参数add是记录根到“叶子”路径上祖先的add之和。
快速序列操作2:
支持操作:
Set(L,R,v) : A[L~R]=v
Query(L,R): 计算[L,R]内的最小值、最大值、区间和。
关于算法:注意到 1 类题目的Add操作对于顺序是没有要求的,而 2 类题目中的Set则是有顺序要求,如果顺序改变结果亦会改变。所以泛泛地说:任意两个Set操作不能出现祖先后代的关系。所以必要时需要把Set下传给子结点。 但也可以找到set是祖先后辈关系的反例,这种情况是两操作并未“相遇”的结果,因为只有“相遇”才会pushdown。要知道处于上方的操作是下方操作之后标记的,于是加入递归边界1:有无set标记的判断。
作者所给同时支持Add与Set操作的模板:
联系题目:UVa11992
链接:
注:如果代码中包含中文字符会乱码 ┑( ̄Д  ̄)┍
一、线段树+点修改
支持操作:
Update(x,v): 将Ax修改为v
Query(L,R) : 计算[L,R]内的最小值
int minv[maxn]; int ql,qr; int Query(int u,int L,int R){ int M=L + (R-L)/2 , ans=INF; if(ql<=L && R<=qr) return minv[u]; if(ql <= M) ans=min(ans,Query(2*u,L,M)); if(M < qr) ans=min(ans,Query(2*u+1,M+1,R)); return ans; } int p,v; //A[p]=v void Update(int u,int L,int R){ if(L==R) {minv[u]=v; return; } //叶节点则修改 int M=L+(R-L)+1; if(p<=M) Update(2*u,L,M); else Update(2*u+1,M+1,R); //递归p所在子树 minv[u]=min(minv[2*u],minv[2*u+1]); //更新当前minv }
联系题目:LA3938
链接:
二、[b]线段树+区间修改
[/b]
快速序列操作1:
支持操作:
Add(L,R,v):
Query(L,R):计算[L,R]内的最小值、最大值、区间和。
关于算法:在 一 的基础上增加了addv,这种为了避免复杂操作而打标记的方法与链表题UVa12657相通,新增maintain维护结点信息。需要注意的是Query的新参数add是记录根到“叶子”路径上祖先的add之和。
int minv[maxn],addv[maxn],sumv[maxn],maxv[maxn]; //maintain维护u的结点信息 void maintain(int u,int L,int R){ int lc=2*u,rc=2*u+1; minv[u]=maxv[u]=sumv[u]=0; //叶子结点的设置 if(L<R){ //有子树 minv[u]=min(minv[lc],minv[rc]); maxv[u]=max(maxv[lc],maxv[rc]); sumv[u]=sumv[lc]+sumv[rc]; } minv[u] += addv[u]; maxv[u] += addv[u]; sumv[u] += addv[u]*(R-L+1); //亦 叶子结点 //考虑到add } int y1,y2,v; //A[y1~y2] += v void Add(int u,int L,int R){ if(y1<=L && R<=y2) addv[u] +=v; else{ int lc=2*u,rc=2*u+1; int M=L+(R-L)+1; if(y1 <= M) Add(lc,L,M); if(M < y2) Add(rc,M+1,R); } maintain(u,L,R); //递归结束后维护u结点信息 } int ql,qr; int _min=INF,_max=-INF,_sum=0; int Query(int u,int L,int R,int add){ //要考虑到祖先的add if(ql<=L && R<=qr) { _min=min(_min,minv[u]+add); _max=max(_max,maxv[u]+add); _sum += sumv[u]+ add*(R-L+1); } else{ int M=L+(R-L)/2; if(ql <= M) Query(u*2,L,M,add+addv[u]); //add+addv[u]保证是根->叶该路径上的祖先add之和 if(M < qr) Query(u*2+1,M+1,R,add+addv[u]); } }
快速序列操作2:
支持操作:
Set(L,R,v) : A[L~R]=v
Query(L,R): 计算[L,R]内的最小值、最大值、区间和。
关于算法:注意到 1 类题目的Add操作对于顺序是没有要求的,而 2 类题目中的Set则是有顺序要求,如果顺序改变结果亦会改变。所以泛泛地说:任意两个Set操作不能出现祖先后代的关系。所以必要时需要把Set下传给子结点。 但也可以找到set是祖先后辈关系的反例,这种情况是两操作并未“相遇”的结果,因为只有“相遇”才会pushdown。要知道处于上方的操作是下方操作之后标记的,于是加入递归边界1:有无set标记的判断。
int minv[maxn],setv[maxn],sumv[maxn],maxv[maxn]; //maintain维护u的结点信息 void maintain(int u,int L,int R){ int lc=2*u,rc=2*u+1; minv[u]=maxv[u]=sumv[u]=0; //叶子结点的设置 if(L<R){ //有子树 minv[u]=min(minv[lc],minv[rc]); maxv[u]=max(maxv[lc],maxv[rc]); sumv[u]=sumv[lc]+sumv[rc]; } if(setv[u] >= 0) { minv[u]=maxv[u]=setv[u];sumv[u]=setv[u]*(R-L+1);} } void pushdown(int u){ int lc=u*2,rc=u*2+1; if(setv[u]>=0){ setv[lc]=setv[rc]=setv[u]; //下传 -1表示无set setv[u]=-1; } } int y1,y2,v; //A[y1~y2] += v void Set(int u,int L,int R){ if(y1<=L && R<=y2) setv[u]=v; else{ pushdown(u); //下传set int lc=2*u,rc=2*u+1; int M=L+(R-L)+1; if(y1 <= M) Set(lc,L,M); else maintain(lc,L,M); if(M < y2) Set(rc,M+1,R); else maintain(rc,M+1,R); //针对不能递归的子树的两次maintain 维护信息 } maintain(u,L,R); //递归结束后维护u结点信息 } int ql,qr; int _min=INF,_max=-INF,_sum=0; int Query(int u,int L,int R){ if(setv[u]>=0){ //递归边界1 有set标记 //注意sum范围 _sum += setv[u] * (min(R,qr)-max(L,ql)+1); _min=min(_min,minv[u]); _max=max(_max,maxv[u]); } else if(ql<=L && R<=qr){ //递归边界2 边界区间 且 此区间没有受到set的影响 _sum += sumv[u]; _min=min(_min,minv[u]); _max=max(_max,maxv[u]); } else{ int M=L+(R-L)/2; if(ql<=M) Query(u*2,L,M); if(M < qr) Query(u*2+1,M+1,R); } }
作者所给同时支持Add与Set操作的模板:
struct IntervalTree { int sumv[maxnode], minv[maxnode], maxv[maxnode], setv[maxnode], addv[maxnode]; // 维护信息 void maintain(int o, int L, int R) { 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); } if(addv[o]) { minv[o] += addv[o]; maxv[o] += addv[o]; sumv[o] += addv[o] * (R-L+1); } } // 标记传递 void 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; setv[o] = -1; // 清除本结点标记 } if(addv[o]) { addv[lc] += addv[o]; addv[rc] += addv[o]; addv[o] = 0; // 清除本结点标记 } } void update(int o, int L, int R) { int lc = o*2, rc = o*2+1; if(y1 <= L && y2 >= R) { // 标记修改 if(op == 1) addv[o] += v; else { setv[o] = v; addv[o] = 0; } } else { pushdown(o); int M = L + (R-L)/2; if(y1 <= M) update(lc, L, M); else maintain(lc, L, M); if(y2 > M) update(rc, M+1, R); else maintain(rc, M+1, R); } maintain(o, L, R); } void query(int o, int L, int R, int add) { if(setv[o] >= 0) { int v = setv[o] + add + addv[o]; _sum += v * (min(R,y2)-max(L,y1)+1); _min = min(_min, v); _max = max(_max, v); } else if(y1 <= L && y2 >= R) { _sum += sumv[o] + add * (R-L+1); _min = min(_min, minv[o] + add); _max = max(_max, maxv[o] + add); } else { int M = L + (R-L)/2; if(y1 <= M) query(o*2, L, M, add + addv[o]); if(y2 > M) query(o*2+1, M+1, R, add + addv[o]); } } };
联系题目:UVa11992
链接:
注:如果代码中包含中文字符会乱码 ┑( ̄Д  ̄)┍
相关文章推荐
- 【暑假】[实用数据结构]范围最小值问题(RMQ)
- python数据结构
- 【暑假】[实用数据结构]动态连续和查询问题
- 数据结构(严蔚敏、吴伟民)——读书笔记-1、绪论
- 数据结构基础(18) --哈希表的设计与实现
- 数据结构基础(17) --二叉查找树的设计与实现
- 数据结构基础(16) --树与二叉树
- 数据结构基础(15) --基数排序
- 数据结构基础(14) --链式队列的设计与实现
- 数据结构基础(13) --链式栈的设计与实现
- 数据结构基础(12) --双向循环链表的设计与实现
- 数据结构基础(11) --循环链表的设计与实现
- 数据结构基础(10) --单链表迭代器的设计与实现
- 数据结构基础(9) --单链表的设计与实现(2)之高级操作
- 数据结构基础(8) --单链表的设计与实现(1)之基本操作
- 数据结构基础(7) --循环队列的设计与实现
- 数据结构基础(6) --顺序栈的设计与实现
- 数据结构基础(5) --归并排序
- 数据结构基础(4) --快速排序
- 数据结构基础(3) --Permutation & 插入排序