1 条题解
-
0
自动搬运
来自洛谷,原作者为

BigSmall_En
time舟从此逝,code海寄余生搬运于
2025-08-24 22:35:15,当前版本为作者最后更新于2022-07-14 13:16:51,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
LG8024 [ONTAK2015] Stumilowy sad 题解
因为不会势能分析,不知道大部分题解的方法的复杂度是多少,于是写了一个严格 的做法。
此外,这题不区间加和维护区间最大值的话就是这题。
题意
一个长度为 的序列 。进行 种操作
- 操作 :将 的所有数
- 操作 :将 的所有数对 取
- 操作 :将 的所有数对 取
- 操作 :查询 内数的最大值
$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.区间取
和 比较好理解。
对于 而言,如果 ,那么取 变大的值也会对 取 。如果 那么取 变大的值也不会因为对 取 收到影响。(感觉在说废话,其实也很好理解)
inline void pushctag(int p,ll x){ t[p].maxv=min(t[p].maxv,x); t[p].btag=min(t[p].btag,x); t[p].ctag=min(t[p].ctag,x); } void update_cut(int p,int l,int r,ll h){ if(l<=t[p].left&&t[p].right<=r) return pushctag(p,h); pushdown(p); if(l<=t[ls].right)update_cut(ls,l,r,h); if(r>=t[rs].left)update_cut(rs,l,r,h); pushup(p); }4.区间取
原理和区间取 类似。
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; }
完整代码就不贴了。
后记
之前题解有些地方格式和内容写错了,现已修改。
同时把代码改成了使用函数完成对信息的修改,这样更加简洁。
- 1
信息
- ID
- 7375
- 时间
- 3000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者