牛客挑战赛65 D.233求min(二维偏序 树状数组/cdq分治)

题目

给出长度为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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值