F. Array Partition【单调栈+思维(2100

题意:给你(1e5)长的数组,找任意一个满足以下条件的三元组

 分析:很直观的想法,如果暴力枚举+区间优化是N2的,利用要求2的值相等的条件来优化一层循环,直接枚举数的值;

所以:先利用单调栈预处理出每个数的最大最小的左右区间,然后在存储的时候把所有数以《值,位置》用map的方式存下来,然后改变枚举方式,只枚举个数大于等于3的数的位置,加上stl的复杂度可知均摊是Onlogn的;

如何判断存在有解:对于当前枚举的数,发现第一个位置的左值必须是1,同时最后一个位置必须是n——然后枚举中间的位置,只要存在某个位置的左值-1<=第一个值最大的右值,同时右值>=最后一个值的左值,再细节处理一下,这题就过了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+15;
const int mo = 998244353;
#define pb push_back
#define pii pair<int,int>
#define ft first
#define sd second
#define ffor(i,a,b,c) for(int i=(a);i<(b);i+=(c))
#define For(i,a,b,c) for(int i=(a);i<=(b);i+=(c))
#define rfor(i,a,b,c) for(int i=(a);i>(b);i-=(c))
#define Rfor(i,a,b,c) for(int i=(a);i>=(b);i-=(c))
#define all(x) (x).begin(), (x).end()
#define debug1(x) cerr<<"! "<<x<<endl;
#define debug2(x,y) cerr<<"#  "<<x<<" "<<y<<endl;

void slv(){
	int n;cin>>n;
	vector<int>a(n+1);
	map<int,vector<int>>mp;
	For(i,1,n,1) {
		cin>>a[i];
		mp[a[i]].pb(i);
	}
	stack<pii>st;
	vector<pii>mn(n+1); //单调栈就搞清两个事情:一个是维护单调增还是单调减的栈&&正着扫还是倒着扫
	vector<pii>mx(n+1);
	For(i,1,n,1){
		while(!st.empty()&&st.top().sd>a[i]){
			auto u=st.top(); st.pop();
			mn[u.ft].sd=i-1;
		}
		st.push({i,a[i]});
	}
	while(!st.empty()){
		auto u=st.top(); st.pop();
		mn[u.ft].sd=n;
	}
	Rfor(i,n,1,1){
		while(!st.empty()&&st.top().sd>a[i]){
			auto u=st.top(); st.pop();
			mn[u.ft].ft=i+1;
		}
		st.push({i,a[i]});
	}
	while(!st.empty()){
		auto u=st.top(); st.pop();
		mn[u.ft].ft=1;
	}
	
	For(i,1,n,1){
		while(!st.empty()&&st.top().sd<a[i]){
			auto u=st.top(); st.pop();
			mx[u.ft].sd=i-1;
		}
		st.push({i,a[i]});
	}
	while(!st.empty()){
		auto u=st.top(); st.pop();
		mx[u.ft].sd=n;
	}
	Rfor(i,n,1,1){
		while(!st.empty()&&st.top().sd<a[i]){
			auto u=st.top(); st.pop();
			mx[u.ft].ft=i+1;
		}
		st.push({i,a[i]});
	}
	while(!st.empty()){
		auto u=st.top(); st.pop();
		mx[u.ft].ft=1;
	}
	// For(i,1,n,1) cout<<mx[i].ft<<" ++ "<<mx[i].sd<<'\n';
	// cout<<endl;
	// For(i,1,n,1) cout<<mn[i].ft<<" -- "<<mn[i].sd<<'\n';
	// cout<<endl;
	int flg=0;
	vector<int>ans(3);
	for(auto u:mp)if(u.sd.size()>=3){
		if(flg) break;
		vector<int>tmp=u.sd;
		int sz=tmp.size();
		int x=tmp[0];
		int y=tmp[sz-1];
		if(mx[y].sd!=n || mx[x].ft>1) continue;
		ffor(i,1,sz-1,1){
			if(mn[tmp[i]].ft-1<=mx[x].sd && mn[tmp[i]].sd+1>=mx[y].ft){
				flg=1;
				ans[0]=min(mx[x].sd,tmp[i]-1);
				ans[1]=max(mx[y].ft,tmp[i]);
				ans[1]=min({ans[1],mn[tmp[i]].sd,y-1});
				ans[2]=n-ans[1];
				break;
			}
		}
	}
	if(flg){
		cout<<"YES\n";
		cout<<ans[0]<<" "<<ans[1]-ans[0]<<" "<<ans[2]<<'\n';
		return;
	}
	cout<<"NO\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int t;cin>>t;
	while(t--){
		slv();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值