题解 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;
}

浙公网安备 33010602011771号