题解 正睿1308 【省选特训11】弗福斯

题目链接

经过观察,不难发现好序列的一些性质。

性质一:一个序列是好序列,当且仅当\(\max-\min+1=r-l+1\)。其中\(\max\)表示序列里的最大值,\(\min\)表示序列里的最小值。

性质二:如果两个好序列相交,其交叉部分一定也是好序列。

证明:设交叉部分的最大、最小值分别为\(\max\), \(\min\)。如果交叉部分不是好序列,则一定存在至少一个\([\min,\max]\)之间的数,位置位于交叉部分以外。左边的好序列,说明这些数全部在交叉部分的左边;右边的好序列,说明这些数全部在交叉部分的右边。出现矛盾。故交叉部分一定是好序列。

考虑对每个位置\(i\),求出一个\(R[i]\),表示以\(i\)为左端点的所有好序列,最大右端点为\(R[i]\)。那么对于一组询问\([x,y]\),我们求出最大的一个\(i\),使得\(i\leq x\)\(R[i]\geq y\),则这个\(i\)就是该询问答案的左端点。理由:

  • 首先,\([i,R[i]]\)一定是一个好序列。如果以\([i,R[i]]\)为答案,则答案的左端点是\(i\)
  • 根据定义,一定不存在\(j>i\)使得有\(j\)开头的好序列。
  • 如果存在一个\(j<i\),满足\([j,k]\)是好序列且完全包含\([x,y]\),要使\([j,k]\)\([i,R[i]]\)更优,则必有\(k<R[i]\)。此时,根据性质二,\([i,k]\)也是一个好序列。所以答案的左端点还是\(i\)

这样,我们预处理出\(R[i]\)以后,用ST表维护\(R\)序列的区间最大值。每次询问,二分\(i\)的位置,判断\((\max_{j=\text{mid}}^{x}R[j])\)是否\(\geq y\)即可。

同理,如果我们预处理出\(L[i]\),则找到第一个\(j\geq y\)使得\(L[j]\leq x\),这样的\(j\)就是答案的右端点了。

如何求出\(R[1\dots n]\)\(L[1\dots n]\)呢?不妨以求\(R\)为例。把序列翻转后用同样的方法做一遍,就能求出\(L\)了。

考虑求\(R\)。从后往前扫描所有左端点\(i\)。对于每个\(j\geq i\),记\(\text{mx}_j=\max_{k=i}^{j}p_k\), \(\text{mn}_j=\min_{k=i}^{j}p_k\),则显然有:\(\forall j\geq i:\text{mx}_j-\text{mn}_j\geq j-i\)。而且根据性质一,\([i,j]\)是好序列,当且仅当\(\text{mx}_j-\text{mn}_j=j-i\)。所以,如果我们能对所有\(j\),维护出\(v_j=\text{mx}_j-\text{mn}_j-j\),那么只需要维护\(v\)序列的区间最小值和最小值的位置,值相同时取靠后的位置,就能求出\(R[i]\)了。注意,我们询问时询问\([i,n]\)的区间最小值,这个最小值一定是\(v_i=-i\)。所以我们关心的其实并不是最小值本身,而是最后一个等于最小值的位置,这个位置就是\(R[i]\)

因为是从后往前扫描\(i\),考虑从\(i+1\)变为\(i\)时,要做的操作相当于是让所有\(j\)\(\text{mx}_j\)\(p_i\)\(\max\)\(\text{mn}_j\)\(p_i\)\(\min\)。同时维护出\(v_j=\text{mx}_j-\text{mn}_j-j\)的区间最小值及其位置。这个既取\(\max\)又取\(\min\)的操作无法直接用吉老师线段树实现,考虑更巧妙的方法。我们维护两个单调栈,一个栈里数值单调递增,表示每个数后面第一个大于它的数出现的位置;另一个栈单调递减,表示每个数后面第一个小于它的数出现的位置。则对于递增栈里相邻的两个元素\(\text{sta}[i]\), \(\text{sta}[i-1]\),序列上\([\text{sta}[i],\text{sta}[i-1]-1]\)这段区间的区间最大值就是\(p_{\text{sta}[i]}\)。同理,对于递减栈,这样的情况代表的是区间最小值。当我们要把\(i\)入栈时,需要分别从两个栈顶弹出一些元素,以此维持栈的单调性。这些弹出的元素,意味着我们要把对应区间的最大/最小值修改为\(p_i\)。并且现在要修改的量都是已知的了。我们直接用线段树做区间加即可。

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

参考代码:

//problem:ZR1308
#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;

const int MAXN=1e5;
const int INF=1e9;
int n,a[MAXN+5],L[MAXN+5],R[MAXN+5];

struct SegmentTree{
	// min{ max-min-pos }
	// 值相等时,选靠后的
	int mn[MAXN*4+5],mnp[MAXN*4+5],tag[MAXN*4+5];
	void push_up(int p){
		if(mn[p<<1]<mn[p<<1|1]){
			mn[p]=mn[p<<1];
			mnp[p]=mnp[p<<1];
		}
		else{
			mn[p]=mn[p<<1|1];
			mnp[p]=mnp[p<<1|1];
		}
	}
	void upd(int p,int v){
		mn[p]+=v;
		tag[p]+=v;
	}
	void push_down(int p){
		if(tag[p]){
			upd(p<<1,tag[p]);
			upd(p<<1|1,tag[p]);
			tag[p]=0;
		}
	}
	void build(int p,int l,int r){
		tag[p]=0;
		if(l==r){
			mn[p]=-l;
			mnp[p]=l;
			return;
		}
		int mid=(l+r)>>1;
		build(p<<1,l,mid);
		build(p<<1|1,mid+1,r);
		push_up(p);
	}
	void range_add(int p,int l,int r,int ql,int qr,int v){
		if(ql<=l&&qr>=r){
			upd(p,v);
			return;
		}
		push_down(p);
		int mid=(l+r)>>1;
		if(ql<=mid)range_add(p<<1,l,mid,ql,qr,v);
		if(qr>mid)range_add(p<<1|1,mid+1,r,ql,qr,v);
		push_up(p);
	}
	pii better_one(pii x,pii y){
		if(x.fi==y.fi)return x.se>y.se?x:y;
		return x.fi<y.fi?x:y;
	}
	pii query_rangemin(int p,int l,int r,int ql,int qr){
		if(ql<=l&&qr>=r){
			return mk(mn[p],mnp[p]);
		}
		push_down(p);
		int mid=(l+r)>>1;
		pii res=mk(INF,-1);
		if(ql<=mid)res=query_rangemin(p<<1,l,mid,ql,qr);
		if(qr>mid)res=better_one(res,query_rangemin(p<<1|1,mid+1,r,ql,qr));
		push_up(p);
		return res;
	}
	SegmentTree(){}
}T;

void get_R(int n,const int* a,int* res){
	//res[i]: 使a[i...j]是好序列的最大的j
	static pii sta_le[MAXN+5],sta_gr[MAXN+5];
	int top_le=0,top_gr=0;
	sta_le[top_le=1]=mk(-INF,n+1);
	sta_gr[top_gr=1]=mk(INF,n+1);
	T.build(1,1,n);
	for(int i=n;i>=1;--i){
		while(sta_le[top_le].fi>=a[i]){
			assert(top_le>1);
			if(sta_le[top_le].fi-a[i]){
				T.range_add(1,1,n,sta_le[top_le].se,sta_le[top_le-1].se-1,sta_le[top_le].fi-a[i]);
			}
			--top_le;
		}
		sta_le[++top_le]=mk(a[i],i);
		while(sta_gr[top_gr].fi<=a[i]){
			assert(top_gr>1);
			if(a[i]-sta_gr[top_gr].fi){
				T.range_add(1,1,n,sta_gr[top_gr].se,sta_gr[top_gr-1].se-1,a[i]-sta_gr[top_gr].fi);
			}
			--top_gr;
		}
		sta_gr[++top_gr]=mk(a[i],i);
		res[i]=T.query_rangemin(1,1,n,i,n).se;
	}
}
void get_L(int n,const int* a,int* res){
	static int rev[MAXN+5];
	for(int i=1;i<=n;++i)rev[i]=a[n-i+1];
	get_R(n,rev,res);
	reverse(res+1,res+n+1);
	for(int i=1;i<=n;++i)res[i]=n-res[i]+1;
}

int _log2[MAXN+5];
struct RMQ{
	int mx[MAXN+5][18],mn[MAXN+5][18];
	void build(int n,int* arr){
		for(int i=1;i<=n;++i)mx[i][0]=mn[i][0]=arr[i];
		for(int j=1;j<=17;++j){
			for(int i=1;i+(1<<(j-1))<=n;++i){
				mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
				mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
			}
		}
	}
	int qmax(int l,int r){
		assert(l<=r);
		int k=_log2[r-l+1];
		return max(mx[l][k],mx[r-(1<<k)+1][k]);
	}
	int qmin(int l,int r){
		assert(l<=r);
		int k=_log2[r-l+1];
		return min(mn[l][k],mn[r-(1<<k)+1][k]);
	}
	RMQ(){}
}rmqL,rmqR;

int main() {
	_log2[0]=-1;
	for(int i=1;i<=MAXN;++i)_log2[i]=_log2[i>>1]+1;
	cin>>n;
	for(int i=1;i<=n;++i)cin>>a[i];
	get_R(n,a,R);
	get_L(n,a,L);
	//for(int i=1;i<=n;++i)cout<<L[i]<<" ";cout<<endl;
	rmqL.build(n,L);
	rmqR.build(n,R);
	int q;cin>>q;while(q--){
		int x,y,ansl,ansr;cin>>x>>y;
		int l=1,r=x;
		while(l<r){
			int mid=(l+r+1)>>1;
			if(rmqR.qmax(mid,x)>=y)l=mid;
			else r=mid-1;
		}
		ansl=l;
		l=y,r=n;
		while(l<r){
			int mid=(l+r)>>1;
			if(rmqL.qmin(y,mid)<=x)r=mid;
			else l=mid+1;
		}
		ansr=l;
		cout<<ansl<<' '<<ansr<<endl;
	}
	return 0;
}
posted @ 2020-04-18 20:58  duyiblue  阅读(60)  评论(0编辑  收藏  举报