P7706 「Wdsr-2.7」文文的摄影布置
题意
给定长度为 \(n\) 的数组 \(a\) 和 \(b\),支持单点修改,\(q\) 次区间查询 \(\max_{l\le i<k\le r} \{a_i + a_k - \min_{i<j<k}b_j\}\)。
\(n,q\le 5\times10^5,1\le a_i,b_i\le10^8\qquad\text{2s,256MB}\)
题解
考虑使用线段树维护信息。记 \(\psi(p)\) 表示线段树上节点 \(p\) 的答案,接下来要考虑如何根据左右子树的答案,更新当前节点的答案。
注意到答案和 \(i,j,k\) 三个位置有关,我们希望 \(a_i,a_k\) 尽量的大,\(b_j\) 尽量小,那么就需要维护区间 \(a\) 最大值,\(b\) 最小值。
然后是分类讨论,我们讨论 \(\psi(p)\) 最大时,它的 \(i,j,k\) 会落在什么位置:
- \(i,j,k\) 均在左子树或均在右子树:直接取左右子树答案最大值。\(\psi(p) = \max(\psi(ls),\psi(rs))\)。
- \(i,j\) 在左子树,\(k\) 在右子树:\(k\) 直接选右子树中 \(b_k\) 最大的,对于 \(i,j\),需要的是 \(\max_{i<j}a_i - b_j\)。
- \(i\) 在左子树,\(j,k\) 在右子树:同上,\(i\) 选最大的,\(j,k\) 是 \(\max_{j<k}- b_j + a_k\)。
发现还需要维护 \(\max_{i<j}a_i - b_j\) 这样的信息,继续分类讨论:
- \(i,j\) 在同一子树中:取左右子树最大值。
- \(i\) 在左子树,\(j\) 在右子树:\(i\) 取左子树 \(a_i\) 最大值,\(j\) 取右子树 \(b_j\) 最小值。
对于 \(\max_{j < k}-b_j + a_k\),同样的讨论。
记第一种信息为 \(L(p)\),第二种信息为 \(R(p)\),\(a\) 最大为 \(Max(p)\),\(b\) 最小为 \(Min(p)\),我们就能整合出以下的式子:
一环套一环,太牛了哥!
单点修改就是直接对 \(Min,Max\) 进行修改,更新信息;查询就把所有区间像上面的方式合并起来,复杂度是一只 \(\log\) 的。
时间复杂度 \(\mathcal{O}(n\log n)\),分块 \(\mathcal{O}(n\sqrt{n})\) 卡卡应该能过。
代码实现中,我们可以采取重载 \(+\) 运算进行信息的维护,这样就可以模仿区间加的方式维护信息:
XDT operator+ (XDT A){ XDT res; res.l = min(l,A.l); res.r = max(r,A.r); res.Max = max(Max,A.Max); res.Min = min(Min,A.Min); res.L = max(max(L,A.L),Max - A.Min); res.R = max(max(R,A.R),- Min + A.Max); res.Ans = max(max(Ans,A.Ans),max(L + A.Max,Max + A.R)); return res; }
完整代码如下:
#include <bits/stdc++.h> using namespace std; const int N = 5e5 + 5; int n,q,a[N],b[N]; struct XDT{ int l,r; int Ans,Max,Min,L,R; XDT() { Ans = Max = Min = L = R = - 1e9; } XDT operator+ (XDT A){ XDT res; res.l = min(l,A.l); res.r = max(r,A.r); res.Max = max(Max,A.Max); res.Min = min(Min,A.Min); res.L = max(max(L,A.L),Max - A.Min); res.R = max(max(R,A.R),- Min + A.Max); res.Ans = max(max(Ans,A.Ans),max(L + A.Max,Max + A.R)); return res; } }t[N << 2]; #define ls p << 1 #define rs p << 1 | 1 void pushup(int p){ t[p] = t[ls] + t[rs]; } void build(int p,int l,int r){ if (l == r){ t[p].l = t[p].r = l; t[p].Max = a[l]; t[p].Min = b[l]; return ; } int mid = (l + r) >> 1; build(ls,l,mid), build(rs,mid + 1,r); pushup(p); } void modify1(int p,int x,int y){ if (t[p].l == t[p].r){ t[p].Max = y; return ; } int mid = (t[p].l + t[p].r) >> 1; if (x <= mid) modify1(ls,x,y); else modify1(rs,x,y); pushup(p); } void modify2(int p,int x,int y){ if (t[p].l == t[p].r){ t[p].Min = y; return ; } int mid = (t[p].l + t[p].r) >> 1; if (x <= mid) modify2(ls,x,y); else modify2(rs,x,y); pushup(p); } void query(int p,int l,int r,XDT &ans){ if (l <= t[p].l && t[p].r <= r) return (void)(ans = ans + t[p]); int mid = (t[p].l + t[p].r) >> 1; if (l <= mid) query(ls,l,r,ans); if (r > mid) query(rs,l,r,ans); } int read(){ int x = 0; char ch = getchar(); while (ch < '0' || ch > '9'){ ch = getchar(); } while ('0' <= ch && ch <= '9'){ x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar(); } return x; } int main(){ n = read(), q = read(); for (int i = 1;i <= n;i++) a[i] = read(); for (int i = 1;i <= n;i++) b[i] = read(); build(1,1,n); while (q--){ int o = read(), l = read(), r = read(); XDT ans; if (o == 1) modify1(1,l,r); if (o == 2) modify2(1,l,r); if (o == 3) query(1,l,r,ans), printf("%d\n",ans.Ans); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!