• 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;
    }
    
    
  • 相关阅读:
    我的访问量咋才3万了
    Khronos发布WebGL标准规范 1.0
    X3Dom V1.2发布
    解决Linux(Fedora Ubuntu)笔记本的待机休眠
    多用户虚拟Web3D环境Deep MatrixIP9 1.04发布
    网络科技公司Web开发团队管理的小结
    XamlReader 动态加载XAML
    Excel Data Reader开源的.NET excel读取库
    .net Sql server 事务的两种用法
    通过使用客户端证书调用 Web 服务进行身份验证{转}
  • 原文地址:https://www.cnblogs.com/jz-597/p/13822245.html
Copyright © 2020-2023  润新知