线性dp+单调栈 Codeforces Round #622 (Div. 2) C Skyscrapers

Skyscrapers (hard version)

This is a harder version of the problem. In this version n≤500000

The outskirts of the capital are being actively built up in Berland. The company “Kernel Panic” manages the construction of a residential complex of skyscrapers in New Berlskva. All skyscrapers are built along the highway. It is known that the company has already bought n plots along the highway and is preparing to build n skyscrapers, one skyscraper per plot.

Architects must consider several requirements when planning a skyscraper. Firstly, since the land on each plot has different properties, each skyscraper has a limit on the largest number of floors it can have. Secondly, according to the design code of the city, it is unacceptable for a skyscraper to simultaneously have higher skyscrapers both to the left and to the right of it.

Formally, let’s number the plots from 1 to n. Then if the skyscraper on the i-th plot has ai floors, it must hold that ai is at most mi (1≤ai≤mi). Also there mustn’t be integers j and k such that j

The company wants the total number of floors in the built skyscrapers to be as large as possible. Help it to choose the number of floors for each skyscraper in an optimal way, i.e. in such a way that all requirements are fulfilled, and among all such construction plans choose any plan with the maximum possible total number of floors.

Input
The first line contains a single integer n (1≤n≤500000) — the number of plots.

The second line contains the integers m1,m2,…,mn (1≤mi≤109) — the limit on the number of floors for every possible number of floors for a skyscraper on each plot.

Output
Print n integers ai — the number of floors in the plan for each skyscraper, such that all requirements are met, and the total number of floors in all skyscrapers is the maximum possible.

If there are multiple answers possible, print any of them.


dp+单调栈;

我们用b[i]表示答案数组;

dp[i][0]表示以 i 为终点,一个单调递增的 b[1-i] 的和的最大值;

dp[i][1]表示以 i 为起点,一个单调递减的 b[i-n] 的和的最大值;

所以状态转移方程为:

如果:a[i]>a[i-1]
dp[i][0]=dp[i-1][0]+a[i]
否则:
dp[i][0]=dp[ l[i] ][0]+(i-l[i])*a[i];

dp[i][1]类似;

这里的终点就是l[i],r[i]数组了,表示的是在左边和右边第一个比a[i]元素小的位置;

这个可以用单调栈求解;(单调栈经典问题)

代码:

#include
#define ll long long
#define pa pair
#define lson k<<1
#define rson k<<1|1
#define inf 0x3f3f3f3f
//ios::sync_with_stdio(false);
using namespace std;
const int N=500100;
const int M=1000100;
const ll mod=998244353;
int n;
ll dp[N][2];
ll a[N];
int sta[N];
int l[N],r[N];
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	int cnt=0;
	for(int i=1;i<=n;i++){
		while(cnt&&a[sta[cnt]]>=a[i]){
			r[sta[cnt]]=i;//右边第一个小元素
			cnt--;
		}
		sta[++cnt]=i;
		if(cnt!=1) l[i]=sta[cnt-1];
	}
	if(l[n]==0&&r[n]==0){
		for(int i=n-1;i>=1;i--){
			if(a[i]==a[n]){
				l[n]=i;
				break;
			}
		}
	}
	for(int i=1;i<=n;i++){//单调递增 
		if(a[i]>=a[i-1]) dp[i][0]=dp[i-1][0]+a[i];
		else dp[i][0]=dp[l[i]][0]+(ll)(i-l[i])*a[i];
	}
	for(int i=n;i>=1;i--){//单调递减 
		if(a[i]>=a[i+1]) dp[i][1]=dp[i+1][1]+a[i];
		else if(r[i]) dp[i][1]=dp[r[i]][1]+(ll)(r[i]-i)*a[i];
		else dp[i][1]=dp[r[i]][1]+(ll)(n-i+1)*a[i];
	} 
	ll ans=0;
	int pos=0;
	for(int i=1;i<=n;i++){
		if(dp[i][0]+dp[i][1]-a[i]>ans){
			ans=dp[i][0]+dp[i][1]-a[i];
			pos=i;
		}
	}
	for(int i=pos-1;i>=1;i--){
		if(a[i]>a[i+1]) a[i]=a[i+1];
	}
	for(int i=pos+1;i<=n;i++){
		if(a[i]>a[i-1]) a[i]=a[i-1];
	} 
	for(int i=1;i<=n;i++) cout<<a[i]<<" ";
	cout<<endl;
	return 0;
}

你可能感兴趣的:(#,线性dp,#,codeforces上分记录)