题目
给出长度为n(n<=1e5)的序列a1,a2,...,an和b1,b2,...,bn(-1e3<=ai,bi<=1e3),
请你找出1≤l≤r≤n,使得min(al+al+1+...+ar, bl+bl+1+...+br)最大,并输出这个值
思路来源
官方题解
LYC_music submission
题解
两种做法,
第一种做法,是按值域w二分,
二分之后,验证答案是否成立时,是一个裸的二维偏序,
复杂度O(nlogwlogn)
第二种做法,
是把min拆开,分两种情况讨论,看哪半边小
即分别讨论sum1[r]-sum1[l-1]<=sum2[r]-sum2[l-1]①
以及sum1[r]-sum1[l-1]>sum2[r]-sum2[l-1]②
统计两次贡献,
比如①,把sum2[i]-sum1[i]看成是一维,记为xi
即(i,xi)要去统计j<=i,xj<=xi的二维偏序,这样的点j对点i的贡献是sum1[i]-sum1[j]
因为是对称的,所以交换sum1和sum2之后,再跑一次
因为有l-1,所以下标从[0,n]
复杂度O(nlogn)
转化成二维偏序之后,
在线:树状数组,离线:cdq分治,典中典
树状数组:
(x,y)两维都很大的时候,考虑把x离散化成下标,y仍然按值存
cdq分治:
先按x排增序,按y归并排序,
在排序过程中考虑x小的值对x大的值的贡献,并令排序后的部分按y呈增序
这里各给出本题O(nlogn)的cdq和bit方式的实现,
代码1(cdq)
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,INF=0x3f3f3f3f;
int n,ans;
struct node{
int a,b;
}e[N],f[N],g[N];
void cdq(node *f,int l,int r){
if(l==r)return;
int mid=(l+r)/2;
cdq(f,l,mid);
cdq(f,mid+1,r);
int i=l,j=mid+1,s=l,mn=INF;
for(;i<=mid && j<=r;){
if(f[i].a-f[i].b<=f[j].a-f[j].b){
mn=min(mn,f[i].b);
g[s++]=f[i++];
}
else{
ans=max(ans,f[j].b-mn);
g[s++]=f[j++];
}
}
for(;i<=mid;){
g[s++]=f[i++];
}
for(;j<=r;){
ans=max(ans,f[j].b-mn);
g[s++]=f[j++];
}
for(int i=l;i<=r;++i){
f[i]=g[i];
}
}
int main(){
scanf("%d",&n);
ans=-INF;
for(int i=1;i<=n;++i){
scanf("%d",&e[i].a);
e[i].a+=e[i-1].a;
}
for(int i=1;i<=n;++i){
scanf("%d",&e[i].b);
e[i].b+=e[i-1].b;
f[i]={e[i].b,e[i].a};
}
cdq(e,0,n);
cdq(f,0,n);
printf("%d\n",ans);
return 0;
}
代码2(bit)
树状数组下标不能是0,
那令n++,就起到了加一个0元素的作用
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,inf=0x3f3f3f3f;
int n,tr[N],a[N],b[N],p[N];
int ans=-inf;
void add(int x,int y){
for(;x<N;x+=x&-x){
tr[x]=min(tr[x],y);
}
}
int ask(int x){
int res=inf;
for(;x;x-=x&-x){
res=min(res,tr[x]);
}
return res;
}
void solve(){
sort(p+1,p+n+1,[&](int x,int y){
return b[x]-a[x]<b[y]-a[y];
});
for(int i=1;i<=n;i++)tr[i]=inf;
int l=1;
for(int i=1;i<=n;i++){
int u=p[i];
while(l<=n&&b[p[l]]-a[p[l]]<=b[u]-a[u]){
add(p[l],a[p[l]]);
l++;
}
int Y=ask(u-1);
ans=max(a[u]-Y,ans);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
a[i+1]=a[i]+x;
}
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
b[i+1]=b[i]+x;
}
n++;
for(int i=1;i<=n;i++)p[i]=i;
solve();
for(int i=1;i<=n;i++)swap(a[i],b[i]);
solve();
printf("%d\n",ans);
return 0;
}