gym102920 L. Two Buildings(决策单调性+分治)

题意:

给定长度为n的数组h,
要求计算max{(h[i]+h[j])*(j-i)},其中i<j.

数据范围:n<=1e6,1<=h(i)<=1e6

解法:
(h[i]+h[j])*(j-i)
=(h[j]-(-h[i]))*(j-i)

令a[i]=h[i],b[i]=-h[i],
那么可以转化为计算(a[j]-b[i])*(j-i),
转化为(i,a[i]),(j,b[j])在二维坐标系中围成的矩形面积.

对于一个固定的(j,a[j]),选择的(i,b[i])显然位置右下越优.
那么对于(i,b[i])(j,b[j]),如果i<j且b[i]>b[j],
那么(i,b[i])完全可以删去,
去掉b[]无用点之后,剩下的点一定是对于i<j,有b[i]<b[j],
同理a[]去掉无用点之后,剩下的点一定是对于i<j,有a[i]>b[j].

那么坐标图大概是这样的:
在这里插入图片描述

对于a[1,n],设mid=(l1+r1)/2,
设点(mid,a[mid])在点(pos,b[pos])处获得最大值.

如图示:
在这里插入图片描述

那么对于(mid,a[mid])左边的点(j,a[j]),
他们的最优解点(i,b[i])一定满足i<=pos,
可以用反证法证明,
大概就是如果i>pos,那么(mid,a[mid)的就不是(pos,b[pos]),
画一下图应该也能证明出来.
假设现在我们已经求出(mid,a[mid])(pos,b[pos])配对最优,
根据我们上面的结论,我们只需要继续分别计算:
1.a[l1,mid-1]和b[l2,pos]匹配的最大值.
2.a[mid+1,r2]h和b[pos,r2]匹配的最大值.(mid,a[mid])(pos,b[pos])的值,三者取max就是答案,
其中情况1和情况2是子问题,可以递归计算.
code:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e6+5;
struct Node{
    int x,y;
}a[maxm],b[maxm];
int del[maxm];
int h[maxm];
int n,m;
bool cmp(Node a,Node b){
    if(a.x==b.x)return a.y<b.y;
    return a.x<b.x;
}
int cal(int j,int i){
    return (a[j].y-b[i].y)*(a[j].x-b[i].x);
}
int solve(int l1,int r1,int l2,int r2){
    if(l1>r1||l2>r2)return -1e18;
    int mid=(l1+r1)/2;
    int pos=l2;
    int ma=cal(mid,pos);
    for(int i=l2+1;i<=r2;i++){
        int temp=cal(mid,i);
        if(temp>=ma){
            ma=temp;
            pos=i;
        }
    }
    return max(ma,max(solve(l1,mid-1,l2,pos),solve(mid+1,r1,pos,r2)));
}
signed main(){
    ios::sync_with_stdio(0);
    cin>>n;
    m=n;
    for(int i=1;i<=n;i++){
        cin>>h[i];
        a[i]={i,h[i]};
        b[i]={i,-h[i]};
    }
    sort(a+1,a+1+n,cmp);
    sort(b+1,b+1+n,cmp);
    //去掉a数组无用数据
    for(int i=1;i<=n;i++)del[i]=0;
    int last=n,cnt=0;
    for(int i=n-1;i>=1;i--){
        if(a[i].y<=a[last].y){
            del[i]=1;
        }else{
            last=i;
        }
    }
    for(int i=1;i<=n;i++){
        if(!del[i]){
            a[++cnt]=a[i];
        }
    }
    n=cnt;
    //去掉b数组无用数据
    for(int i=1;i<=m;i++)del[i]=0;
    last=1,cnt=0;
    for(int i=2;i<=m;i++){
        if(b[i].y>=b[last].y){
            del[i]=1;
        }else{
            last=i;
        }
    }
    for(int i=1;i<=m;i++){
        if(!del[i]){
            b[++cnt]=b[i];
        }
    }
    m=cnt;
    //分治计算答案
    int ans=solve(1,n,1,m);
    cout<<ans<<endl;
    return 0;
}

  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值