洛谷P2422 良好的感觉

本题我想了好久,最后在豆包的帮助下成功AC

题目

传送门

题意概述

在长度为N的正整数序列里寻找一段区间[i,j],使得这个区间里最小的数乘上这个区间的和的值最大

思路

由于我们是正整数,那么我们要使得区间尽可能的大,也就是我们可以分别给每一个数都找一个区间,使得这个区间以这个数为最小值,并且这个区间要尽可能的大,又因为我们求的是区间问题,所以我们可以使用前缀和来帮助我们计算

实现

如果找这个区间?

我们可以先定义一个l数组和r数组来分别表示这个数组左边最靠近a[i]并且小于a[i]的数和这个数组右边最靠近并且小于等于a[i]的数,这里为什么要错开符号主要是为了不重复和遗漏区间.
这里的数据结构我使用的是单调栈
如果没有找到l[i]就使用0,如果没有找到r[i]就使用n+1

//左边
for(int i=1;i<=n;i++){
		while(!q.empty()&&a[i]<=a[q.top()]){
			q.pop(); 
		}
		if(q.empty())l[i]=0;
		else l[i]=q.top();
		q.push(i); 
	}
while(!q.empty())q.pop();//清空
//右边
for(int i=n;i>=1;i--){
		while(!q.empty()&&a[i]<a[q.top()]){
			q.pop(); 
		}
		if(q.empty())r[i]=n+1;
		else r[i]=q.top();
		q.push(i);
	}

怎么求最大值?

我们可以遍历每个a[i]并且用他们的区间来求最大值

for(int i=1;i<=n;i++){
		int ll=l[i]+1;
		int rr=r[i]-1;
		ans=max(ans,(s[rr]-s[ll-1])*a[i]);
	}

总代码

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100005],l[100005],r[100005]; 
long long ans=0,s[100005];
stack<int>q; 
int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	for(int i=1;i<=n;i++){
		while(!q.empty()&&a[i]<=a[q.top()]){
			q.pop(); 
		}
		if(q.empty())l[i]=0;
		else l[i]=q.top();
		q.push(i); 
	}
	while(!q.empty())q.pop();
	for(int i=n;i>=1;i--){
		while(!q.empty()&&a[i]<a[q.top()]){
			q.pop(); 
		}
		if(q.empty())r[i]=n+1;
		else r[i]=q.top();
		q.push(i);
	}
	for(int i=1;i<=n;i++){
		int ll=l[i]+1;
		int rr=r[i]-1;
		ans=max(ans,(s[rr]-s[ll-1])*a[i]);
	}
	cout<<ans;
	return 0;
} 
posted @ 2026-08-02 23:24  陈晨2011  阅读(4)  评论(0)    收藏  举报