题解 P2659 【美丽的序列】

P2659 美丽的序列

题目大意:

给出一个序列,找出一个子区间,使该区间的最小值与区间长度乘积最大。

solution:

我们可以枚举最小值:对于每一个数列中的数字 \(a_i\) ,找到当它作为数列最小值时最长的序列,分别用数组 \(zuo[i]\)\(you[i]\) 保存这时 的左右端点。

以上操作我们可以用单调栈实现,正序找第一个比 $a[ $ $ i $ $ ]$ 小的位置 \(-1\) 后就是右端点,栈里还有找不到比它小的数,把 \(you[\) \(i\) $ ] $ 赋成 \(n\) ,倒序 \(+1\) 后就是左端点,剩余 $zuo[ $ $ i $ $ ]$ 赋成 \(1\)
最后 \(\max \limits_{i=1}^{n} (you[i]-zuo[i]+1)\times a[i]\)

接下来是细节的处理:

乘积可能大于 \(\text{int}\) 的范围,要开 \(\text{long long }\)

看到这的同学,可以自己去写代码了(tf口吻)

单调栈代码
#include<cstdio>
using namespace std;
const int N=2e6+5;
typedef long long LL; 
int st[N],top,z[N],y[N];
LL a[N];
LL ans;
LL Max(LL x,LL y){return x>y?x:y;}
int main(){
	int n;scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	for(int i=1;i<=n;i++){
		while(top&&a[i]<a[st[top]])
			y[st[top--]]=i-1;
		st[++top]=i;
	}
	while(top) y[st[top--]]=n;
	for(int i=n;i>=1;i--){
		while(top&&a[i]<a[st[top]])
			z[st[top--]]=i+1;
		st[++top]=i;
	}
	while(top) z[st[top--]]=1;
	for(int i=1;i<=n;i++)
		ans=Max(ans,a[i]*(y[i]-z[i]+1));
	printf("%lld",ans);
	return 0;
}

笛卡尔树代码
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=2e6+5;
int n; 
LL a[N];
int st[N],top;
int ls[N],rs[N];
inline void build(){
	for(int i=1;i<=n;i++){
		while(top&&a[st[top]]>a[i])
			ls[i]=st[top--];
		rs[st[top]]=i;
		st[++top]=i;
	}
}
int y[N],z[N];
void dfs(int x){
	z[x]=y[x]=x;
	if(ls[x]){
		dfs(ls[x]);
		z[x]=z[ls[x]];
	}
	if(rs[x]){
		dfs(rs[x]);
		y[x]=y[rs[x]];
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	build();
	int rt;
	while(top) rt=st[top--];
	dfs(rt);
	LL ans=0;
	for(int i=1;i<=n;i++)
		ans=max(ans,1ll*a[i]*(y[i]-z[i]+1));	
	printf("%lld",ans);
	return 0;
}

End

posted @ 2021-07-29 19:42  Mr_think  阅读(65)  评论(0)    收藏  举报