洛谷 P7706 「Wdsr-2.7」文文的摄影布置
前言(本质是废话)
晚自习打了一个小时打完这道题,写篇题解纪念一下。
这道题的思路还是比较巧妙的,且有亿一点点绕,请务必确保读者在阅读时没有头晕、恶心、困倦等症状,出现笔者概不负责。
拉回正题
给定两个长为 \(n\) 的序列 \(A\) 和 \(B\),每次进行下面三种操作之一:
-
将点 \(a\) 的 \(A\) 值更改为 \(b\);
-
将点 \(a\) 的 \(B\) 值更改为 \(b\);
-
查询区间 \(a\) 到 \(b\) 的 \(max\{A[i]-B[j]+A[k]\}, (a \leq i<j<k \leq b)\);
首先一眼线段树啊,设计 \(ans\) 表示线段树需要维护的操作三所求的区间一坨玩意的最大值,\(mid\) 在下文表示该区间的中间位置。那么还是使用经典的分类讨论:
-
若三个取值位置都 \(\leq\) \(mid\),则直接继承左区间的 \(ans\);
-
若三个取值位置都 \(>\) \(mid\),则直接继承右区间的 \(ans\);
-
若三个值的位置跨越了左右两个区间,情况就会复杂
很多一点。因为三个位置满足 \(a \leq i<j<k \leq b\),又由于三个值跨越了左右两个区间,很显然就有左右两个区间中的一个会剩下一个 \(A\) 值。那这时在这半边区间里就没有什么位置限制了,当然是取最大值为最优,故线段树还需要维护区间 \(A\) 最大值。那么考虑进一步分类讨论:-
若剩下的两个值 \(A_i\) 和 \(B_j\) 均位于左区间,而 \(A_k\) 位于右区间,则不难发现线段树还需要维护一个值 \(maxP\) 来记录当 \(p<q\) 时 \(A_p-B_q\) 的最大值。那怎么维护这个
诡异的东西呢?那又是分类讨论一下:-
若 \(A_i\) 与 \(B_j\) 都位于左区间的左区间,那么就是左区间的左区间的 \(maxP\);
-
若 \(A_i\) 与 \(B_j\) 都位于右区间的右区间,则为右区间的右区间的 \(maxP\);
-
若分别在两边(因为有 \(i<j\),所以只可能是 \(i\) 在左边,\(j\) 在右边),为了求最大值,猜都猜得出来就是加的大减的小嘛。所以 \(maxP\) 就为左区间的 \(maxA\) 减去右区间的 \(minB\);
-
-
若剩下的两个值 \(A_k\) 和 \(B_j\) 均位于右区间,而 \(A_i\) 位于左区间,则线段树就需要维护一个值 \(maxQ\) 来记录当 \(p>q\) 时 \(A_p-B_q\) 的最大值。维护方法同上,请读者自己思考
(主要是笔者懒得写了)。
-
那么解题的核心思路就讲完了,剩下的都是一些线段树的细节边角料。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int A[500005],B[500005];
struct node{
int L,R,maxA,minB,maxP,maxQ,ans;
};
node tree[4000005];
void pushup(node &u,node ls,node rs){
u.maxA=max(ls.maxA,rs.maxA),u.minB=min(ls.minB,rs.minB);
u.maxP=max(max(ls.maxP,rs.maxP),ls.maxA-rs.minB);
u.maxQ=max(max(ls.maxQ,rs.maxQ),rs.maxA-ls.minB);
u.ans=max(max(ls.ans,rs.ans),max(ls.maxA+rs.maxQ,ls.maxP+rs.maxA));
return;
}
void buildtree(int u,int l,int r){
tree[u].L=l,tree[u].R=r;
if(l==r){
tree[u].maxA=A[l],tree[u].minB=B[l];
tree[u].maxP=tree[u].maxQ=tree[u].ans=INT_MIN;
return;
}
int mid=(l+r)/2;
buildtree(u*2,l,mid),buildtree(u*2+1,mid+1,r);
pushup(tree[u],tree[u*2],tree[u*2+1]);
return;
}
void mergeA(int u,int id,int val){
if(tree[u].L==id&&tree[u].R==id){
tree[u].maxA=val;
tree[u].maxP=tree[u].maxQ=tree[u].ans=INT_MIN;
return;
}
int mid=(tree[u].L+tree[u].R)/2;
if(id<=mid) mergeA(u*2,id,val);
else mergeA(u*2+1,id,val);
pushup(tree[u],tree[u*2],tree[u*2+1]);
return;
}
void mergeB(int u,int id,int val){
if(tree[u].L==id&&tree[u].R==id){
tree[u].minB=val;
tree[u].maxP=tree[u].maxQ=tree[u].ans=INT_MIN;
return;
}
int mid=(tree[u].L+tree[u].R)/2;
if(id<=mid) mergeB(u*2,id,val);
else mergeB(u*2+1,id,val);
pushup(tree[u],tree[u*2],tree[u*2+1]);
return;
}
node query(int u,int l,int r){
int L=tree[u].L,R=tree[u].R;
if(R<l||L>r) return {0,0,INT_MIN,INT_MAX,INT_MIN,INT_MIN,INT_MIN};
if(L>=l&&R<=r) return tree[u];
int mid=(L+R)/2;
if(r<=mid) return query(u*2,l,r);
if(l>mid) return query(u*2+1,l,r);
node left=query(u*2,l,r);
node right=query(u*2+1,l,r);
node res;
pushup(res,left,right);
return res;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&A[i]);
for(int i=1;i<=n;i++) scanf("%d",&B[i]);
buildtree(1,1,n);
for(int i=1;i<=m;i++){
int opt,a,b;
scanf("%d%d%d",&opt,&a,&b);
if(opt==1) mergeA(1,a,b);
else if(opt==2) mergeB(1,a,b);
else printf("%d\n",query(1,a,b).ans);
}
return 0;
}
\(-\) ❀ 完结撒花 ❀ \(-\)

浙公网安备 33010602011771号