题解 CF1332G No Monotone Triples

时间:2020-04-01
本文章向大家介绍题解 CF1332G No Monotone Triples,主要包括题解 CF1332G No Monotone Triples使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

考虑一个没有Monotone Triples的序列具有哪些性质。

  • 当序列长度为\(3\)时,一定是以下两种情况之一:
    • \(a_1<a_2,a_2>a_3\)。即先上升,后下降。至于\(a_1,a_3\)的大小关系,没有要求。
    • \(a_1>a_2,a_2<a_3\)。即先下降,后上升。至于\(a_1,a_3\)的大小关系,没有要求。
  • 当序列长度为\(4\)时,设四个元素的相对大小为\(1,2,3,4\)。那么一定是\(1,4\)在中间两个位置,\(2,3\)分别在两边的位置。形式化地说,我们要求:\(\max(a_2,a_3)>\max(a_1,a_4),\min(a_2,a_3)<\min(a_1,a_4)\)
  • 当序列长度大于等于\(5\)时,序列中一定存在Monotone Triples。因为最小值或最大值中,至少有一个值,它的某一边有\(\geq3\)个元素。这三个元素要么自己构成Monotone Triples,要么其中的两个必和这个最小/最大值构成Monotone Triples。(ps:上面长度为\(4\)的序列的性质也是用这一思路来证明的)

所以,问题转化为判断,区间内有没有长度为\(4\)的合法子序列,有没有长度为\(3\)的合法子序列。可以求出以每个点\(i\)作为左端点,长度为\(4\)的合法子序列的最小右端点,记为\(ans4[i]\)。然后对\(ans4\)做后缀最小值。则一次询问\(l,r\),就是要判断\(ans4[l]\)是否\(\leq r\)\(ans3\)同理。

问题转化为对每个\(i\),如何求\(ans3[i]\),\(ans4[i]\)

先考虑长度为\(3\)的子序列。我们可以用线段树上二分,找到\(i\)右边第一个\(a_x>a_i\)的位置\(x\),再找到\(x\)右边第一个\(a_y<a_x\)的位置\(y\)。同理,还要考虑\(a_x<a_i,a_y>a_x\)的情况。两种情况的\(y\)的较小值,就是\(ans3[i]\)了。如果不想写线段树上二分,也可以用单调栈实现。这与我们下面要讲的求长度为\(4\)的子序列的方法是类似的。

考虑长度为\(4\)的子序列。我们从右向左枚举\(i\)。维护两个单调栈。从栈顶到栈底,第一个栈元素的值单调下降,第二个栈的元素值单调上升。这样,我们可以求出每个\(i\)右边第一个值小于它的位置、值大于它的位置。记这两个位置分别为\(x,y\)。我们找到\(\max(x,y)\)右边,第一个不在任何一个单调栈中的位置,记为\(z\)。则\(ans4[i]=z\)。因为显然\([i+1,z-1]\)中一定存在元素大于\(\max(a_i,a_z)\),也一定存在元素小于\(\min(a_i,a_z)\)

右边第一个不在任何一个单调栈中的位置,可以简单地用数据结构维护出来(如set)。找\(x,y\),如果序列里没有重复元素,则显然\(x,y\)就分别是两个栈在\(i\)加入之前的栈顶。如果有重复元素,我们可以在单调栈里把等于\(a_i\)的那一段元素二分掉,接下来的位置上就是\(x,y\)了。至于我们为什么要把相等的数都存在单调栈里(即让栈里元素不严格上升/下降),这是为了防止序列出现\(a_z=a_x\)\(a_z=a_y\)的情况。结合下面这组数据或许更容易理解。

//input:
5 1
2 1 2 3 3
1 5
//expect output:
3
1 2 3
//如果不让栈内元素非严格上升/下降,会输出:
4
1 2 4 5

时间复杂度\(O(n\log n)\)

参考代码:

//problem:CF1332G
#include <bits/stdc++.h>
using namespace std;
 
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
 
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
 
namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
	if(S==T){
		T=(S=buf)+fread(buf,1,MAXN,stdin);
		if(S==T)return EOF;
	}
	return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
	#define getchar Fread::getchar
#endif
template<typename T>inline void read(T& x){
	x=0;int f=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+(ch-'0'),ch=getchar();
	x*=f;
}
/*  ------  by:duyi  ------  */ // myt天下第一
const int MAXN=2e5;
int n,q,a[MAXN+5];
struct Ans3{
	int p1,p2,p3;
	Ans3(){p3=MAXN+1;}
}ans3[MAXN+5];
struct Ans4{
	int p1,p2,p3,p4;
	Ans4(){p4=MAXN+1;}
}ans4[MAXN+5];
struct SegmentTree{
	int mx[MAXN*4+5],mn[MAXN*4+5];
	void build(int p,int l,int r){
		if(l==r){
			mx[p]=mn[p]=a[l];
			return;
		}
		int mid=(l+r)>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		mx[p]=max(mx[p<<1],mx[p<<1|1]);
		mn[p]=min(mn[p<<1],mn[p<<1|1]);
	}
	int _nxt_bigger(int p,int l,int r,int v){
		if(l==r){assert(mx[p]>v);return l;}
		int mid=(l+r)>>1;
		if(mx[p<<1]>v)return _nxt_bigger(p<<1,l,mid,v);
		else return _nxt_bigger(p<<1|1,mid+1,r,v);
	}
	int nxt_bigger(int p,int l,int r,int pos,int v){
		if(mx[p]<=v)return n+1;
		if(l>=pos)return _nxt_bigger(p,l,r,v);
		int mid=(l+r)>>1,res=n+1;
		if(pos<=mid)res=nxt_bigger(p<<1,l,mid,pos,v);
		if(res!=n+1)return res;
		else return nxt_bigger(p<<1|1,mid+1,r,pos,v);
	}
	int _nxt_smaller(int p,int l,int r,int v){
		if(l==r){assert(mn[p]<v);return l;}
		int mid=(l+r)>>1;
		if(mn[p<<1]<v)return _nxt_smaller(p<<1,l,mid,v);
		else return _nxt_smaller(p<<1|1,mid+1,r,v);
	}
	int nxt_smaller(int p,int l,int r,int pos,int v){
		if(mn[p]>=v)return n+1;
		if(l>=pos)return _nxt_smaller(p,l,r,v);
		int mid=(l+r)>>1,res=n+1;
		if(pos<=mid)res=nxt_smaller(p<<1,l,mid,pos,v);
		if(res!=n+1)return res;
		else return nxt_smaller(p<<1|1,mid+1,r,pos,v);
	}
	SegmentTree(){}
}T;
int sta1[MAXN+5],sta2[MAXN+5],top1,top2,insta[MAXN+5];
/*
sta1 栈内元素,从栈顶(top)到栈底(1),a[i]的值(非严格)递减
sta2 栈内元素,从栈顶(top)到栈底(1),a[i]的值(非严格)递增
*/
int main() {
	read(n);read(q);
	for(int i=1;i<=n;++i)read(a[i]);
	T.build(1,1,n);
	for(int i=n-2;i>=1;--i){
		int x=T.nxt_bigger(1,1,n,i,a[i]),y=n+1;
		if(x<n)y=T.nxt_smaller(1,1,n,x,a[x]);
		if(y!=n+1){
			ans3[i].p1=i;
			ans3[i].p2=x;
			ans3[i].p3=y;
		}
		x=T.nxt_smaller(1,1,n,i,a[i]),y=n+1;
		if(x<n)y=T.nxt_bigger(1,1,n,x,a[x]);
		if(y<ans3[i].p3){
			ans3[i].p1=i;
			ans3[i].p2=x;
			ans3[i].p3=y;
		}
		if(i!=n-2&&ans3[i+1].p3<ans3[i].p3)ans3[i]=ans3[i+1];
	}
	
	set<int>s;//不在任何一个栈中的元素
	for(int i=n;i>=1;--i){
		while(top1){
			int p=sta1[top1];
			if(a[p]>a[i]){
				insta[p]--;
				if(!insta[p])s.insert(p);
				top1--;
			}
			else break;
		}
		while(top2){
			int p=sta2[top2];
			if(a[p]<a[i]){
				insta[p]--;
				if(!insta[p])s.insert(p);
				top2--;
			}
			else break;
		}
		int p1=0,p2=0;
		
		int l=0,r=top1;
		while(l<r){
			int mid=(l+r+1)>>1;
			if(a[sta1[mid]]<a[i])l=mid;
			else r=mid-1;
		}
		p1=l;
		
		l=0,r=top2;
		while(l<r){
			int mid=(l+r+1)>>1;
			if(a[sta2[mid]]>a[i])l=mid;
			else r=mid-1;
		}
		p2=l;
		
		if(p1&&p2){
			//cout<<i<<endl;
			set<int>::iterator it=s.upb(max(sta1[p1],sta2[p2]));
			if(it!=s.end()){
				l=1,r=p1;
				while(l<r){
					int mid=(l+r)>>1;
					if(sta1[mid]<(*it))r=mid;
					else l=mid+1;
				}
				p1=sta1[l];
				l=1,r=p2;
				while(l<r){
					int mid=(l+r)>>1;
					if(sta2[mid]<(*it))r=mid;
					else l=mid+1;
				}
				p2=sta2[l];
				ans4[i].p1=i;
				ans4[i].p2=min(p1,p2);
				ans4[i].p3=max(p1,p2);
				ans4[i].p4=(*it);
			}
		}
		
		if(ans4[i+1].p4<ans4[i].p4)ans4[i]=ans4[i+1];
		
		insta[i]=2;
		sta1[++top1]=i;
		sta2[++top2]=i;
	}
	while(q--){
		int l,r;
		read(l);read(r);
		if(ans4[l].p4<=r){
			printf("%d\n%d %d %d %d\n",4,ans4[l].p1,ans4[l].p2,ans4[l].p3,ans4[l].p4);
		}
		else if(ans3[l].p3<=r){
			printf("%d\n%d %d %d\n",3,ans3[l].p1,ans3[l].p2,ans3[l].p3);
		}
		else{
			puts("0\n");
		}
	}
	return 0;
}

原文地址:https://www.cnblogs.com/dysyn1314/p/12613641.html