题目
思路
涉及到区间操作并且是静态的区间,用线段树是再合适不过的
如果直接维护每个点的权值不麻烦
但是要维护历史最大值就十分麻烦
所以我们转化下思路
线段树上维护操作
接着我们思考如何将操作统一化,并且是可叠加的,这样才能方便用懒标记
我们设标记 \((a,b)\)表示将x变为 \(max(x+a,b)\)
区间加:\((a,-INF)\)
区间赋值:\((-INT,a)\)
将每个值变为\(max(x_i-a,0)\)即为:\((-a,0)\)
接着我们考虑合并的问题
假设现在的标记为\((a,b)\),从父亲节点传下来的标记为\((c,d)\)
先将第一个标记表示出来\(max(x+a,b)\),这就是现在的\(x'\),
再将\(x'\) 代入
现在即为\(max(c+max(x+a,b),d)\)
可以发现对于前一项的b,b与x无关
而我们定义标记的前一项是与x有关,而后一项与x无关
所以可以将其提取出来,即为
\(max(c+a+x,max(c+b,d))\)
所以合并之后的标记即为
\((a+c,max(b+c),d)\)
现在已经解决了标记合并的问题
现在我们来考虑最大值的维护
如果我们将标记看成一个函数,
那么这个函数的图像一定是这样的:
也就意味这合并之后的标记也是这样子的
同时注意到前面一段的平板是由标记的第二项决定的
后面一段上升的直线是由第一项决定的
当 \(x_1<x_2\),必然有\(f(x_1)\le f(x_2)\)
所以我们可以直接将现在的标记怼上去就行了
什么意思呢?
如果现在的最大值函数为\(f(x)\)
当前的操作为\(g(x)\)
那么新的最大值函数
\(h(x)=\begin{cases}f(x)[f(x)>g(x)]\\g(x)[其他情况]\end{cases}\)
你看h很复杂,其实不然,如果你将两个函数转换成图像的形式之后
你就发现\(h(x)\)的图像跟标记的函数图像一模一样
所以你就可以对标记的第一项取一个max,对后一项取一个max就行了
更新的时候最好写一个取值的边界的判断条件
笔者因此调了一个上午
别忘了开读入优化,UOJ对cin,cout不太友好,即使关了同步
代码
#include<iostream> #include<cstdio> using namespace std; #define pll pair<long long,long long> #define x first #define y second #define basic make_pair(0,0); struct node { int l; int r; pll val; pll maxx; }tre[2000005]; int n,m; int a[500005]; int opt,l,r,x; void read(int &x) { x=0; int f=1; char c=getchar(); while('0'>c||c>'9') { if(c=='-') f=-1; c=getchar(); } while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } x*=f; } void write(long long x) { if(x>9) write(x/10); putchar(x%10+'0'); } pll operator + (const pll &a,const pll &b) { pll t; t.x=a.x+b.x; t.y=a.y+b.y; return t; } pll update_pair(pll a,pll b) { pll t; t.x=a.x+b.x; t.y=max(a.y+b.x,b.y); return t; } void push_down(int k,int fa) { pll t1=update_pair(tre[k].val,tre[fa].val); pll t2=update_pair(tre[k].val,tre[fa].maxx); tre[k].val.x=max(t1.x,-(1ll<<55)); tre[k].val.y=t1.y; tre[k].maxx.x=max(tre[k].maxx.x,t2.x); tre[k].maxx.y=max(tre[k].maxx.y,t2.y); } void init(int k) { tre[k].val.x=tre[k].maxx.x=0; tre[k].val.y=tre[k].maxx.y=-(1ll<<55); } void build(int l,int r,int k) { tre[k].l=l; tre[k].r=r; tre[k].val=basic; tre[k].maxx=basic; if(l==r) return; int mid=(l+r)>>1; build(l,mid,k<<1); build(mid+1,r,k<<1|1); } void add(int l,int r,int k,pll a) { if(tre[k].l>r||tre[k].r<l) return; if(l<=tre[k].l&&tre[k].r<=r) { pll t1=update_pair(tre[k].val,a); tre[k].val=t1; tre[k].maxx.x=max(tre[k].maxx.x,t1.x); tre[k].maxx.y=max(tre[k].maxx.y,t1.y); return; } push_down(k<<1,k); push_down(k<<1|1,k); init(k); add(l,r,k<<1,a); add(l,r,k<<1|1,a); } pll ask_now(int u,int k) { if(tre[k].l>u||tre[k].r<u) return basic; if(tre[k].l==tre[k].r) return tre[k].val; push_down(k<<1,k); push_down(k<<1|1,k); init(k); return ask_now(u,k<<1)+ask_now(u,k<<1|1); } pll ask_maxx(int u,int k) { if(tre[k].l>u||tre[k].r<u) return basic; if(tre[k].l==tre[k].r) return tre[k].maxx; push_down(k<<1,k); push_down(k<<1|1,k); init(k); return ask_maxx(u,k<<1)+ask_maxx(u,k<<1|1); } signed main() { read(n); read(m); for(int i=1;i<=n;i++) read(a[i]); build(1,n,1); for(int i=1;i<=m;i++) { read(opt); if(opt<=3) { read(l); read(r); read(x); if(opt==1) { add(l,r,1,make_pair(x,-(1ll<<55))); } if(opt==2) { add(l,r,1,make_pair(-x,0)); } if(opt==3) { add(l,r,1,make_pair(-(1ll<<55),x)); } } else { read(x); if(opt==4) { pll t=ask_now(x,1); write(max(a[x]+t.x,t.y)); putchar('\n'); } if(opt==5) { pll t=ask_maxx(x,1); write(max(a[x]+t.x,t.y)); putchar('\n'); } } } return 0; }
来源:https://www.cnblogs.com/loney-s/p/12230251.html