2017-2018 ACM-ICPC, Central Europe Regional Contest (CERC 17) I Intrinsic Interval

题目

给你一个排列,定义一个连续段为一个子区间,其中包含的数是连续的。

对于一个子区间,求出包含它的本源连续段,即包含它的最小的连续段。

需要处理若干个询问。

(n,mle 10^5)


正解

一眼看下去这不就是析合树吗?

于是调了半天析合树,最终艰难地通过了。

当然有些比较阳间的做法:可以发现对于询问的区间([L,R]),找到最小的(r)使得存在(l)([L,R]subseteq [l,r])并且([l,r])为连续段,此时([l,r])即为答案。

反证为什么是对的:如果存在更优的区间([l',r'])(r<r'),那么一定有([l',r])为连续段(两个连续段的交为连续段)。

后面就随便做了:右端点(r)从左到右扫,每个询问挂在右端点上,用个线段树对于每个(l)维护(max_{i=l}^r a_i-min_{i=l}^r+l)最小的(l),如果这个值等于(r),那么([l,r])为连续段。用个(set)存每个询问,如果当前存在最长的连续段([l,r])覆盖了某个询问,那么这个询问答案的右端点就确定了,左端点在线段树上二分就可以出来。

之前(max_{i=l}^r a_i-min_{i=l}^r+l)一直都是维护两个单调栈,然而我AC之后看网上的题解发现一个似乎更方便的做法:一个连续段([l,r])一定满足有(r-l)((x,x+1))满足(x,x+1)都在区间内。考虑统计有多少对。于是在加入某个数(x)的时候,包含(x-1)(x+1)的区间就可以加一。维护个最大值。其它的类似。

时间复杂度都是(O(nlg n))。(不过最正宗的析合树配上tarjan LCA可以做到线性)


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
int n;
int a[N];
int s0[N],t0;
int s1[N],t1;
pair<int,int> v[N*4];
int t[N*4];
pair<int,int> vmin(pair<int,int> x,pair<int,int> y){return x.first<=y.first?x:y;}
void build(int k,int l,int r){
	v[k]={0,l};
	if (l==r) return;
	int mid=l+r>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}
void add(int k,int l,int r,int st,int en,int c){
	if (st<=l && r<=en){
		v[k].first+=c,t[k]+=c;
		return;
	}
	if (t[k]){
		v[k<<1].first+=t[k],t[k<<1]+=t[k];
		v[k<<1|1].first+=t[k],t[k<<1|1]+=t[k];
		t[k]=0;
	}
	int mid=l+r>>1;
	if (st<=mid) add(k<<1,l,mid,st,en,c);
	if (mid<en) add(k<<1|1,mid+1,r,st,en,c);
	v[k]=vmin(v[k<<1],v[k<<1|1]);
}
pair<int,int> res;
void query(int k,int l,int r,int st,int en){
	if (v[k].first>=res.first) return;
	if (st<=l && r<=en){
		res=v[k];
		return;
	}
	if (t[k]){
		v[k<<1].first+=t[k],t[k<<1]+=t[k];
		v[k<<1|1].first+=t[k],t[k<<1|1]+=t[k];
		t[k]=0;
	}
	int mid=l+r>>1;
	if (st<=mid) query(k<<1,l,mid,st,en);
	if (mid<en) query(k<<1|1,mid+1,r,st,en);
}
int f[N];
struct Node{
	Node *fa,*r;
	int mn,mx;
	bool s;//0 xi 1 he
	int L,R;
} d[N*2];
int cnt;
Node *id[N];
bool judge(Node *x,Node *y){
	return x->mx-x->mn+1+y->mx-y->mn+1==max(x->mx,y->mx)-min(x->mn,y->mn)+1;
}
void eat(Node *x,Node *y){
	x->mn=min(x->mn,y->mn);
	x->mx=max(x->mx,y->mx);
	x->L=min(x->L,y->L);
	x->R=max(x->R,y->R);
}
Node *q[N];
int nq;
void insert(Node *x,int l){
	if (!nq || x->L<=l){
		q[++nq]=x;
		return;
	}
	Node *t=q[nq];
	if (judge(t,x)){
		if (t->r && judge(t->r,x)){
			x->fa=t,t->r=x;
			eat(t,x);
			--nq;
			insert(t,l);
		}
		else{
			Node *nw=&d[++cnt];
			*nw={NULL,x,min(t->mn,x->mn),max(t->mx,x->mx),1,t->L,x->R};
			t->fa=x->fa=nw;
			--nq;
			insert(nw,l);
		}
	}
	else{
		t=&d[++cnt];
		x->fa=t;
		*t={NULL,x,x->mn,x->mx,0,x->L,x->R};
		while (nq && q[nq]->R>=l){
			q[nq]->fa=t;
			eat(t,q[nq--]);
			if (t->mx-t->mn+1==t->R-t->L+1){
				insert(t,l);
				break;
			}
		}
	}
}
int dep[N*2],fa[N*2][18];
void getdep(Node *t){
	if (dep[t-d]) return;
	if (t->fa==NULL){
		dep[t-d]=1;
		return;
	}
	getdep(t->fa);
	dep[t-d]=dep[t->fa-d]+1;
	fa[t-d][0]=t->fa-d;
	for (int i=1;1<<i<dep[t-d];++i)	
		fa[t-d][i]=fa[fa[t-d][i-1]][i-1];
}
int LCA(int u,int v){
	if (dep[u]<dep[v]) swap(u,v);
	for (int k=dep[u]-dep[v],i=0;k;k>>=1,++i)
		if (k&1)
			u=fa[u][i];
	if (u==v) return u;
	for (int i=17;i>=0;--i)
		if (fa[u][i]!=fa[v][i])
			u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}
int kth(int u,int k){
	for (int i=0;k;k>>=1,++i)
		if (k&1)
			u=fa[u][i];
	return u;
}
int main(){
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	build(1,1,n);
	for (int i=1;i<=n;++i){
		while (t0 && a[i]<a[s0[t0]]){
			add(1,1,n,s0[t0-1]+1,s0[t0],+a[s0[t0]]);
			--t0;
		}
		add(1,1,n,s0[t0]+1,i,-a[i]);
		s0[++t0]=i;
		while (t1 && a[i]>a[s1[t1]]){
			add(1,1,n,s1[t1-1]+1,s1[t1],-a[s1[t1]]);
			--t1;
		}
		add(1,1,n,s1[t1]+1,i,+a[i]);
		s1[++t1]=i;
		add(1,1,n,i,i,i);
		res={i+1,0};
		query(1,1,n,1,i);
		f[i]=res.second;
	}
//	for (int i=1;i<=n;++i)
//		printf("%d ",f[i]);
//	printf("
");
	for (int i=1;i<=n;++i){
		id[i]=&d[++cnt];
		*id[i]={NULL,NULL,a[i],a[i],0,i,i};
		insert(id[i],f[i]);
//		for (int j=1;j<=nq;++j)
//			printf("%d:[%d,%d] ",q[j]-d,q[j]->L,q[j]->R);
//		printf("
");
	}
	for (int i=1;i<=cnt;++i){
		getdep(&d[i]);
//		printf("%d ",d[i].fa-d);
	}
//	printf("
");
	int T;
	scanf("%d",&T);
	while (T--){
		int l,r;
		scanf("%d%d",&l,&r);
		l=id[l]-d;
		r=id[r]-d;
		int lca=LCA(l,r);
		if (d[lca].s==0)
			printf("%d %d
",d[lca].L,d[lca].R);
		else{
			l=kth(l,dep[l]-dep[lca]-1);
			r=kth(r,dep[r]-dep[lca]-1);
			printf("%d %d
",d[l].L,d[r].R);
		}
	}
	return 0;
}

原文地址:https://www.cnblogs.com/jz-597/p/13822245.html