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

Description

给定一个长为 n n n 的序列 a 1 … n a_{1\dots n} a1n,需要对它进行两种操作共 n − 1 n-1 n1 次。

对一个长度为 l l l 的序列 b 1 … l b_{1\dots l} b1l 进行一次操作将会把序列变为一个长为 l − 1 l-1 l1 的序列 c 1 … l − 1 c_{1\dots l-1} c1l1

  • 操作一中, ∀ i ∈ [ 1 , l ) , c i = max ⁡ ( b i , b i + 1 ) \forall i\in[1,l),c_i=\max(b_i,b_{i+1}) i[1,l),ci=max(bi,bi+1)
  • 操作二中, ∀ i ∈ [ 1 , l ) , c i = min ⁡ ( b i , b i + 1 ) \forall i\in[1,l),c_i=\min(b_i,b_{i+1}) i[1,l),ci=min(bi,bi+1)

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

Solution

比第一题简单。

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

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

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

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

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

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

又发现一个数会被左右两边第一个比它大的数 a l , a r a_l,a_r al,ar 消去,会在 r − 1 − l r-1-l r1l 次操作一后消失。

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

用单调栈找 a l , a r a_l,a_r al,ar O ( n ) O(n) O(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值