• 题解 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),就是要判断(suffixans4[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(nlog 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
    %d %d %d %d
    ",4,ans4[l].p1,ans4[l].p2,ans4[l].p3,ans4[l].p4);
    		}
    		else if(ans3[l].p3<=r){
    			printf("%d
    %d %d %d
    ",3,ans3[l].p1,ans3[l].p2,ans3[l].p3);
    		}
    		else{
    			puts("0
    ");
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    static、final、this、super关键
    细节二:参数、引用类型、实例化
    枚举类型
    单例模式
    细节一:字符串、switch、默认值、数组
    类属性和类方法
    装饰器模式
    TreeSet
    可见参数和增强for以及自动拆装箱
    静态导入
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12613641.html
Copyright © 2020-2023  润新知