CodeForces - 1454F

#include<bits/stdc++.h>
using namespace std; 
//思路:
//1.找到tl,tr
//2.找到中间的位置u,并得到u能延伸的区间[l,r] 
//3.判断tl,tr,l,r关系
 
//各个值的意义 
//int a[]原值,b[]离散,a[]离散化后的值
//int l[i],r[i]储存位置i的值x可以作为区间最小值,所能延申的最大距离 ,即单调栈 栈内从1->n递增 
//int mxl[i],mxr[i],分别为从左右两边到i的最大值 
//vector<int>g[i] 值i出现的各个位置 

//步骤: 
//step1.离散化
//step2.for(x=1->tot) 挨个判断 值x 是否可以
//step3.tl = g[x][0], tr = g[x][g[x].size()-1]
//需满足:mxl[tl]=x && mxr[tr]=x
//step4.for(j=1->g[x].size()-2) 挨个判断 第j个出现的x是否可以 u=g[x][j],u为位置 
//step5.用单调栈预处理得到u作为最小值可以延伸的区间[l,r] l[i],r[i]
//step6.判断tl,tr,l,r关系

//左右一样,此处只讨论左边的情况 
//1.l<tl 可,第一个区间范围:[1,tl] 
//2.l>=tl && mxl[l-1]=x 可,第一个区间范围:[1,l-1]
const int N = 2e5+23 ;
int a[N],b[N] ;
int mxl[N],mxr[N] ;
int l[N],r[N] ;
vector<int>g[N] ;
stack<int>st ;
int main()
{
	int t ;
	scanf("%d",&t) ;
	while(t--)
	{
		//step1.输入+离散 
		int n ;
		scanf("%d",&n) ;
		for(int i = 1 ; i <= n ; i++) 
		{
			scanf("%d",&a[i]) ;
			b[i-1] = a[i] ;
		}
		sort(b,b+n) ;
		int tot = unique(b,b+n)-b ;
		
		while(!st.empty()) st.pop() ;
		int top ;
		mxl[0] = 0 ;
		for(int i = 1 ; i <= n ; i++)
		{
			a[i] = lower_bound(b,b+tot,a[i])-b+1 ;
			
			//step2.预处理mxl[] ,g[] 
			mxl[i] = max(mxl[i-1],a[i]) ;
			g[a[i]].push_back(i) ;
			
			//step3.预处理单调栈l[]
			while(!st.empty() && a[st.top()]>=a[i]) st.pop() ;//保证单调栈单调递减 (栈内增)
			
			if(st.empty()) l[i] = 1 ;
			else l[i] = st.top()+1 ;
			st.push(i) ;
		}
		mxr[n+1] = 0 ;
		while(!st.empty()) st.pop() ;
		for(int i = n ; i >= 1 ; i--)
		{
			//step2.预处理mxr[]
			mxr[i] = max(mxr[i+1],a[i]) ;
			
			//step3.预处理单调栈r[]
			while(!st.empty() && a[st.top()]>=a[i]) st.pop() ;//保证单调栈单调递减 (栈内增)
			
			if(st.empty()) r[i] = n ;
			else r[i] = st.top()-1 ;
			st.push(i) ;
		}
//		for(int i = 1 ; i <= n ; i++) printf("%d ",a[i]) ;
//		puts("") ;
//      for(int i = 1 ; i <= n ; i++) printf("%d ",l[i]) ;
//		puts("") ;
//		for(int i = 1 ; i <= n ; i++) printf("%d ",r[i]) ;
//		puts("") ;

		//step4 选x
		int flag = 0,ans1,ans2,ans3 ;
		for(int i = 1 ; i <= tot ; i++)
		{
			int sz = g[i].size() ;
			int tl = g[i][0] , tr = g[i][sz-1] ;
//			printf("tl=%d tr=%d i=%d\n",tl,tr,i) ;
			if(mxl[tl]!=i || mxr[tr]!=i || sz<3) continue ;
			for(int j = 1 ; j <= sz-2 ; j++)
			{
				int u = g[i][j] ;//u为值i出现的位置 
				if((l[u]<=tl||(l[u]>tl && mxl[l[u]-1]==i)) && (r[u]>=tr||(r[u]<tr && mxr[r[u]+1]==i)))
				{
//					printf("l=%d r=%d u=%d\n",l[u],r[u],u) ;
					ans1 = max(tl,l[u]-1) ;
					ans2 = min(tr-1,r[u])-ans1 ;
					ans3 = n-ans2-ans1 ;
					flag = 1 ;
					break ;
				}
			}
			if(flag) break ;
		} 
		if(flag) 
		{
			puts("YES") ;
		    printf("%d %d %d\n",ans1,ans2,ans3) ;
		}
		else puts("NO") ;
		for(int i = 1 ; i <= tot ; i++) g[i].clear() ;
	}
	return 0 ;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值