<h1>题目描述</h1>
(本人喜欢用 Vjudge 所以就放那里的链接啦。
给定一个序列,要求实现区间求最大值,区间求和,区间对一个数取min。
<h1>解题报告</h1>
开始挖十一集训的坟了......
这一题是吉司机线段树模板题的简化版本。
大家都会维护最大值和和,怎么实现那个取min的操作呢?
我们考虑对于每个区间都先维护出这个区间的最大值 max,次大值 se,最大值出现的次数 cnt。
这样在覆盖的时候分类讨论一下,设这个区间要对x整体取min:
如果当前区间 $max \leq x$ 就不用继续处理了因为这个区间不会改变;
如果当前区间 $ max \geq x $ 并且 $se \leq x $ 那么只需要将这个区间的最大值改一下就可以了。
否则就递归左右子树继续找,最后合并一下就可以了。
合并也是要分类讨论的 $qwq$.
合并注意下要判断两边的max相等的情况,这时候的cnt和se取值都与一般情况不同。
还有一个小问题:我不知道为什么我如果去掉注释里的CLR函数(即memset) 就会提示我MLE?我十分的疑惑,如果有知道的神犇请在评论里告诉我 $qwq$ 谢谢。
另外这个题目还有一个 进阶版本,按照这种分类讨论操作的思想写就可以了,只不过可能代码量会 略 长一些。
<h1>代码实现</h1>
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdlib> #include <cstdio> #include <vector> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define Re register #define LL long long #define CLR(a,b) memset(a,b,sizeof(a)) #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define DEBUG(x) std::cerr << #x << '=' << x << std::endl const int MAXN = 1000000+5; struct SegmentTree New(int ,int ,SegmentTree ,SegmentTree *); struct SegmentTree{ int l,r,max,se,cnt; LL sum; SegmentTree lc,rc; static SegmentTree *build(int l,int r){ int mid = (l + r) >> 1; return l == r ? New(l,r,NULL,NULL) : New(l,r,build(l,mid),build(mid+1,r)); } void pushup(){ if(lc->max > rc->max){ max = lc->max; se = std::max(lc->se,rc->max); cnt = lc->cnt; } if(lc->max == rc->max){ max = lc->max; se = std::max(lc->se,rc->se); cnt = lc->cnt + rc->cnt; } if(lc->max < rc->max){ max = rc->max; se = std::max(lc->max,rc->se); cnt = rc->cnt; } sum = lc->sum + rc->sum; } void cover(int delta){ if(max <= delta) return; sum += 1llcnt(delta-max); max = delta; } void pushdown(){ lc->cover(max); rc->cover(max); } void update(int pos,int x){ if(l == r){ max = sum = x; se = -1;cnt = 1; return; } int mid = (l + r) >> 1; if(pos <= mid) lc->update(pos,x); else rc->update(pos,x); pushup(); } void modify(int L,int R,int x){ if(x >= max) return; if(L == l && R == r && x > se){ cover(x); return; } pushdown(); int mid = (l + r) >> 1; if(R <= mid) lc->modify(L,R,x); else if(L > mid) rc->modify(L,R,x); else lc->modify(L,mid,x),rc->modify(mid+1,R,x); pushup(); } int qmax(int L,int R){ if(L == l && R == r) return max; pushdown(); int mid = (l + r) >> 1; if(R <= mid) return lc->qmax(L,R); if(L > mid) return rc->qmax(L,R); return std::max(lc->qmax(L,mid),rc->qmax(mid+1,R)); } LL qsum(int L,int R){ if(L == l && R == r) return sum; pushdown(); int mid = (l + r) >> 1; if(R <= mid) return lc->qsum(L,R); if(L > mid) return rc->qsum(L,R); return lc->qsum(L,mid) + rc->qsum(mid+1,R); } }pool[MAXN<<2],frog = pool,segt; SegmentTree New(int l,int r,SegmentTree lc,SegmentTree *rc){ SegmentTree *ret = ++frog; ret->l = l;ret->r = r;ret->lc = lc;ret->rc = rc; ret->max = ret->se = -1;ret->cnt = ret->sum = 0; return ret; } inline void Solve(){ frog = pool;//CLR(pool,0); int N,M;scanf("%d%d",&N,&M); segt = SegmentTree::build(1,N); FOR(i,1,N){ int x;scanf("%d",&x); segt->update(i,x); } while(M--){ int opt,l,r; scanf("%d%d%d",&opt,&l,&r); if(opt == 0){ int t;scanf("%d",&t); segt->modify(l,r,t); } if(opt == 1){ printf("%dn",segt->qmax(l,r)); } if(opt == 2){ printf("%lldn",segt->qsum(l,r)); } } } int main(){ int T;scanf("%d",&T); while(T--) Solve(); return 0; }