LG8024 题解
BigSmall_En · · 题解
LG8024 [ONTAK2015] Stumilowy sad 题解
因为不会势能分析,不知道大部分题解的方法的复杂度是多少,于是写了一个严格
此外,这题不区间加和维护区间最大值的话就是这题。
题意
一个长度为
n 的序列a 。进行4 种操作
- 操作
1 :将[l,r] 的所有数+c - 操作
2 :将[l,r] 的所有数对h 取\min - 操作
3 :将[l,r] 的所有数对h 取\max - 操作
4 :查询[l,r] 内数的最大值1\leq n\leq 5\times 10^5,1\leq a_i\leq 10^9,0\leq |c|\leq 500,1\leq h\leq 10^9
题解
对于这种题目一般考虑使用线段树。
对于线段树上的每一个节点,维护以下四个信息:
1.建树
初始时所有节点的
void buildtree(int p,int l,int r){
t[p].left=l,t[p].right=r;//我的习惯写法,维护线段两端的位置,比较常规
t[p].ctag=INF,t[p].btag=-INF;
if(l==r){t[p].maxv=a[l];return;}
int mid=(l+r)>>1;
buildtree(ls,l,mid);buildtree(rs,mid+1,r);
pushup(p);
}
2.区间加
因为每个数都加
inline void pushtag(int p,ll x){
t[p].maxv+=x;t[p].tag+=x;
if(t[p].ctag<INF)t[p].ctag+=x;
if(t[p].btag>-INF)t[p].btag+=x;
}
void update_plus(int p,int l,int r,ll c){
if(l<=t[p].left&&t[p].right<=r)
return pushtag(p,c),void();
pushdown(p);
if(l<=t[ls].right)update_plus(ls,l,r,c);
if(r>=t[rs].left)update_plus(rs,l,r,c);
pushup(p);
}
3.区间取 \min
原理和区间取
inline void pushbtag(int p,ll x){
t[p].maxv=max(t[p].maxv,x);
t[p].btag=max(t[p].btag,x);
t[p].ctag=max(t[p].ctag,x);
}
void update_build(int p,int l,int r,ll h){
if(l<=t[p].left&&t[p].right<=r)
return pushbtag(p,h);
pushdown(p);
if(l<=t[ls].right)update_build(ls,l,r,h);
if(r>=t[rs].left)update_build(rs,l,r,h);
pushup(p);
}
5.上传信息
inline void pushup(int p){
t[p].maxv=max(t[ls].maxv,t[rs].maxv);
}
6.下传标记
原理就是用父亲节点的标记对子节点的每种信息分别进行修改,和修改操作相同,所以子操作也可以用三个修改函数(分别对应更新
inline void pushdown(int p){
pushtag(ls,t[p].tag);pushtag(rs,t[p].tag);
pushbtag(ls,t[p].btag);pushbtag(rs,t[p].btag);
pushctag(ls,t[p].ctag);pushctag(rs,t[p].ctag);
t[p].tag=0,t[p].btag=-INF,t[p].ctag=INF;
}
7.查询最大值
ll query_max(int p,int l,int r){
if(l<=t[p].left&&t[p].right<=r)
return t[p].maxv;
pushdown(p);ll tmp=-INF;
if(l<=t[ls].right)tmp=max(tmp,query_max(ls,l,r));
if(r>=t[rs].left)tmp=max(tmp,query_max(rs,l,r));
return tmp;
}
完整代码就不贴了。
后记
之前题解有些地方格式和内容写错了,现已修改。
同时把代码改成了使用函数完成对信息的修改,这样更加简洁。