2024-09-02 21:07阅读: 10评论: 0推荐: 0

P10878 [JRKSJ R9] 在相思树下 III 题解

Description

给定一个长为 n 的序列 a1n,需要对它进行两种操作共 n1 次。

对一个长度为 l 的序列 b1l 进行一次操作将会把序列变为一个长为 l1 的序列 c1l1

  • 操作一中,i[1,l),ci=max(bi,bi+1)
  • 操作二中,i[1,l),ci=min(bi,bi+1)

给定整数 m,你只能进行至多 m 次操作一。进行 n1 次操作后序列 a 的长度变为 1。你可以任意安排操作的顺序,求最终剩余的数 a1 的最大值。

Solution

比第一题简单。

两个关键性质就是操作一做满 m 次最优,且做完操作一再做操作二。

我们发现操作一每次一定会把当前最小值消去,使最大值数量变多,操作二一定会把当前最大值消去,使最小值数量变多。

先做操作一则能抬高数值下限,做满 m 次使最小值最大,最大值数量最多,此种决策最优。

而在一次操作一前做操作二,不仅会使最小值变多,还会降低数值上限,不优于上种决策。

确定了操作顺序后就完了吗?并不是,还需要找到最小值,而直接模拟是 O(n2) 的。

发现在一次操作一后可能不止最小值会消去,如 [1,3,2,3] 做一次操作一变成 [3,3,3]

又发现一个数会被左右两边第一个比它大的数 al,ar 消去,会在 r1l 次操作一后消失。

然后在在所有不能在 m 次操作一之内消去的数中取最小值即可。

用单调栈找 al,arO(n) 做完。

Code

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[1000010];
int l[1000010],r[1000010];
stack<int> s;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		while(s.size()&&a[s.top()]<=a[i]){
			s.pop();
		}
		l[i]=s.size()?s.top():0;
		s.push(i);
	}
	while(s.size()) s.pop();
	for(int i=n;i>=1;i--){
		while(s.size()&&a[s.top()]<=a[i]){
			s.pop();
		}
		r[i]=s.size()?s.top():n+1;
		s.push(i);
	}
	int minn=1e9;
	for(int i=1;i<=n;i++){
		if(r[i]-1-l[i]>m){
			minn=min(minn,a[i]);
		}
	}
	cout<<minn;
	return 0;
}

本文作者:larryyu_blog

本文链接:https://www.cnblogs.com/larryyu/p/18393536

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   larryyu_blog  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起