gym102920L Two Buildings (2020-2021 ACM-ICPC, Asia Seoul Regional Contest)决策单调性
题意
给定数组\(h\),求\(max_{1\le i<j\le n}(h[i]+h[j])*(j-i)\),\(1\le n,h[i]\le 1e6\)
分析
-
把原式改写为\(max_{1\le i<j\le n}(h[i]-(-h[j]))*(j-i)\),则对于每个平面点\((i,h[i])\),找到它右下角的点\((j,-h[j])\)使得面积最大。
-
对于\(i_1<i_2\),若\(h[i_1]\ge h[i_2]\),则取\(i_2\)的地方取\(i_1\)一定更大,所以可以只取\(i\)从左至右升序排列的点。同理,对于\(j_1<j_2\),若\(-h[j_1]\ge -h[j_2]\),则取\(j_1\)的地方取\(j_2\)一定更大,所以可以只取\(j\)从右至左降序排列的点。
-
对于点\(i\),若取\(j\)时取得最大值,则对于点\(l<i\),在\([1-j]\)取得最大值;则对于点\(r>i\),在\([j-n]\)取得最大值。所以可以利用决策单调性进行分治。
- 对于图中\(l<i\)的点来说,因为\(j\)比\(k\)优,所以\(S_1\ge S_2\),得到\(S_1+S3\gt S_2-S_4\)。则对于\(l\)来说,在\(j\)左侧取得最大值。同理可得点\(r>i\),在\([j-n]\)取得最大值。
- 对于图中\(l<i\)的点来说,因为\(j\)比\(k\)优,所以\(S_1\ge S_2\),得到\(S_1+S3\gt S_2-S_4\)。则对于\(l\)来说,在\(j\)左侧取得最大值。同理可得点\(r>i\),在\([j-n]\)取得最大值。
代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int maxn=1e6+5;
ll h[maxn],a[maxn],b[maxn],ai[maxn],bi[maxn],ans;
void proc(int l,int r,int L,int R)
{
ll Ans=0,id=L,mid=(l+r)/2;
for(ll i=L;i<=R;i++)
{
ll tmp=(a[mid]-b[i])*(bi[i]-ai[mid]);
if(tmp>Ans)
{
Ans=tmp;
id=i;
}
}
ans=max(ans,Ans);
if(l<mid)
proc(l,mid-1,L,id);
if(mid<r)
proc(mid+1,r,id,R);
}
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>h[i];
a[i]=h[i];
b[i]=-h[i];
}
int cnt=1,R=n;
ai[1]=1;
bi[n]=n;
for(int i=2;i<=n;i++)
{
if(a[i]>a[cnt])
{
a[++cnt]=a[i];
ai[cnt]=i;
}
}
for(int i=n-1;i>=1;i--)
{
if(b[i]<b[R])
{
b[--R]=b[i];
bi[R]=i;
}
}
ans=0;
proc(1,cnt,R,n);
cout<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
int t=1;
// cin>>t;
while(t--)
solve();
return 0;
}