1. 程式人生 > 實用技巧 >codeforces 1313 C2 Skyscrapers (hard version) (單調佇列)

codeforces 1313 C2 Skyscrapers (hard version) (單調佇列)

相較於 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("\n");
	
	return 0;
}