题目链接https://ac.nowcoder.com/acm/contest/884/C
题意
给出一段序列,要求出 m a x ( m i n ( a l . . . r ) ∗ s u m ( b l . . . r ) ) max(min(a_{l...r})*sum(b_{l...r})) max(min(al...r)∗sum(bl...r))。
题解
先处理出每个a[i]做最小值的范围。然后就是要求出左右两个区间,前缀和的最大值和最小值。
- O ( n l o g n ) O(nlogn) O(nlogn)的做法是用线段树维护前缀和。
- O ( n ) O(n) O(n)的做法是在笛卡尔树上,维护左右两个区间最大值和最小值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e6+5;
int n,ls[N],rs[N],vis[N],stk[N],rt;
int a[N],b[N];
ll rmax[N],rmin[N];
ll lmax[N],lmin[N];
ll sum[N];
void build(){
int top=0;
for(int i=1;i<=n;i++){
ls[i]=0,rs[i]=0,vis[i]=0;
}
for(int i=1;i<=n;i++){
int k=top;
while(k>0&&a[stk[k-1]]>a[i]) k--;
if(k) rs[stk[k-1]]=i;
if(k<top) ls[i]=stk[k];
stk[k++]=i;
top=k;
}
for(int i=1;i<=n;i++){
vis[ls[i]]=vis[rs[i]]=1;
}
for(int i=1;i<=n;i++){
if(vis[i]==0) rt=i;
}
}
void dfs(int u){
if(u==0) return;
rmax[u]=rmin[u]=sum[u];
lmax[u]=lmin[u]=sum[u-1];
dfs(ls[u]);dfs(rs[u]);
if(rs[u]){
rmax[u]=max(rmax[u],max(lmax[rs[u]],rmax[rs[u]]));
rmin[u]=min(rmin[u],min(lmin[rs[u]],rmin[rs[u]]));
}
if(ls[u]){
lmax[u]=max(lmax[u],max(lmax[ls[u]],rmax[ls[u]]));
lmin[u]=min(lmin[u],min(lmin[ls[u]],rmin[ls[u]]));
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
sum[i]=sum[i-1]+b[i];
}
build();
dfs(rt);
ll ans=0;
for(int i=1;i<=n;i++){
ans=max(ans,max(1ll*(rmax[i]-lmin[i])*a[i],1ll*(rmin[i]-lmax[i])*a[i]));
}
printf("%lld\n",ans);
return 0;
}