相较于 easy version ,本题规模扩大到 50w
考虑优化,
对于每个高楼,我们要找的其实是最靠近且比这座楼矮的楼,
中间的楼都建成这座高楼的高度
所以使用单调队列,求出每座高楼的前缀和与后缀和,
再扫一遍所有高楼,找到楼高最大的答案
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 500010;
int n;
ll a[maxn],pre[maxn],suf[maxn],p[maxn],q[maxn],b[maxn],l[maxn],r[maxn];
ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
int main(){
n = read();
for(int i=1;i<=n;++i) a[i] = read();
int tail = 0;
ll sum = 0;
for(int i=1;i<=n;++i){
while(q[tail] > a[i] && tail > 0){
sum -= 1ll * (p[tail] - p[tail-1]) * q[tail];
--tail;
}
l[i] = p[tail];
q[++tail] = a[i];
p[tail] = i;
sum += 1ll * (p[tail] - p[tail-1]) * a[i];
pre[i] = sum;
}
tail = 0;
sum = 0;
memset(q,0,sizeof(q));
memset(p,0,sizeof(p));
p[0] = n+1;
for(int i=n;i>=1;--i){
while(q[tail] > a[i] && tail > 0){
sum -= 1ll * (p[tail-1] - p[tail]) * q[tail];
--tail;
}
r[i] = p[tail];
q[++tail] = a[i];
p[tail] = i;
sum += 1ll * (p[tail-1] - p[tail]) * a[i];
suf[i] = sum - a[i];
}
ll ans = 0;
int pos;
for(int i=1;i<=n;++i){
if(pre[i] + suf[i] > ans){
ans = pre[i] + suf[i];
pos = i;
}
}
ll h = a[pos];
b[pos] = a[pos];
for(int i=pos-1;i>=1;--i){
if(a[i] < h) h = a[i];
b[i] = h;
}
h = a[pos];
for(int i=pos+1;i<=n;++i){
if(a[i] < h) h = a[i];
b[i] = h;
}
for(int i=1;i<=n;i++){
printf("%lld ",b[i]);
}printf("
");
return 0;
}