模拟赛搬的题,假了一万次,我也不知道咋搞过去的。
食用本题解时需要感性理解每个操作和定义的合理性,最好不要先去想这么搞的必要性。
我们可以对两个数分开考虑,由于两者顺序无影响。为方便这里将 \(L\) 或 \(R\) 数组统一记作上界 \(x\)。
定义一个取 \(\min\) 操作比另一个取 \(\min\) 操作强,当且仅当删去另一个操作后不会对最终答案产生影响。
分析可以发现,其充要条件是前者的上界加上两者之间的变化量(根据位置可能为负)小于等于后者的上界。
形式化的,就是当 \(x_i+pre_j-pre_i\le x_j\) 又记作 \(x_i-pre_i\le x_j-pre_j\) 时,我们认为 \(i\) 强于 \(j\),\(pre\) 数组为 \(D\) 数组的前缀和。
特别的,当\(x_i+pre_j-pre_i=x_j\) 时,我们仅在满足 \(i<j\) 时认为 \(i\) 强于 \(j\),方便排序。
同时,需要预先将 \(L\) 和 \(R\) 数组里的每个值与可能最大值(无前缀操作情况)取 \(\min\),这样可以使得无操作为最弱操作。
那么贪心的想,对于一个操作序列,我们把不强于当前最强操作,且不在序列里的操作选过来一定是不劣的,因为这样不会使当前答案增加,同时还可能使另一个序列答案增大。
显然的,一个操作是最强的当且仅当比他强的都选了另一边,这一性质同时成立。
然后就会有一种做法是枚举一个序列的最强操作,将且仅将比它强的操作选到另一边,可以发现,这就是最强操作为当前操作时的答案最大值,然后将所有最强操作情况答案取 \(\max\) 就是最终答案。
具体维护的话我们可以将两个序列的操作分别从强到弱排序,然后对于其中一个序列的操作从前到后枚举最强操作(还有空操作情况),同时记录必须选到另一边的操作(就是前缀操作)中在另一边最强操作(初始为空操作),两者分别计算答案相加再取最大值即可。
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int x[N],L[N],R[N],y[N],numa[N],numb[N],ida[N],idb[N],n;
bool cmpa(int u,int v){if(L[u]-y[u]!=L[v]-y[v])return L[u]-y[u]<L[v]-y[v];return u<v;
}
bool cmpb(int u,int v){if(R[u]-y[u]!=R[v]-y[v])return R[u]-y[u]<R[v]-y[v];return u<v;
}
signed main(){scanf("%lld",&n);for(int i=1;i<=n;i++)scanf("%lld",&x[i]);for(int i=1;i<=n;i++)scanf("%lld",&L[i]);for(int i=1;i<=n;i++)scanf("%lld",&R[i]);scanf("%lld%lld",&L[0],&R[0]);for(int i=1;i<=n;i++)y[i]=y[i-1]+x[i];for(int i=1;i<=n;i++){L[i]=min(L[i],L[0]+y[i]);R[i]=min(R[i],R[0]+y[i]);}for(int i=1;i<=n;i++)numa[i]=numb[i]=i;sort(numa+1,numa+n+1,cmpa);sort(numb+1,numb+n+1,cmpb);for(int i=1;i<=n;i++){ida[numa[i]]=i;idb[numb[i]]=i;}int ans=0,now=n+1;for(int i=1;i<=n;i++){ans=max(ans,R[numb[now]]+y[n]-y[numb[now]]+y[n]-y[numa[i]]+L[numa[i]]);now=min(now,idb[numa[i]]);}ans=max(ans,R[numb[now]]+y[n]-y[numb[now]]+y[n]+L[0]);printf("%lld\n",ans);return 0;
}