• 析合树


    析合树

    https://www.cnblogs.com/Paul-Guderian/p/11020708.html

    定义

    对于一个排列,称其中一个区间([l,r])为连续段若(r-l=max{[l,r]}-min{[l,r]}),即其中元素排序后权值形成一段连续的区间.

    对于两个相交的连续段,发现它们的并是连续段,它们的交也是连续段.

    所以我们可以找出(O(n))个本源连续段,满足其他所有连续段和本源连续段只有包含关系,且所有连续段都可以被若干本源连续段的并表示.

    这样一来,本源连续段的包含关系就形成了一个树的结构,称其为析合树.

    其中每个节点是析点或合点,满足

    • 叶子是析点
    • 析点的所有非平凡儿子区间(大小不等于1,也不是全集)都不是连续段
    • 合点的所有非平凡儿子区间都是连续段

    如此一来,所有连续段要么是树上的节点,要么是合点的非平凡儿子区间.

    许多关于连续段的问题都可以在析合树上解决.

    构造

    考虑增量构造,从小到大枚举(r),并处理所有右端点为(r)的节点.

    考虑维护一个栈,按栈顶向下从右到左的顺序储存所有还没有父亲的节点.

    定义当前节点为(now),初始(now=[r,r]).并对栈顶元素重复以下判断.

    • 若栈顶元素是合点,则判断(now)是否能成为它的最后一个儿子(对于合点在建立时纪录它第二个儿子的左端点),若能,则令它为(now),右端点变为(r),弹出栈顶
    • 否则,若(now)和栈顶元素可以构造一个连续段,建立一个新的合点作为它们的父亲,令这个新建的点为(now),弹出栈顶
    • 否则,看是否可以找出栈顶向下的一段连续元素,使得它们共同构成了一个连续段,建立一个新的析点作为他们的父亲,令这个新建的点为(now),弹出这些元素.若不能则退出

    分析复杂度,前两种情况都是(O(1))的,第3种情况假如成功,那么复杂度均摊是(O(n))的,但是若失败可能会退化为(O(n^2))

    所以可以先求出(L_i)表示以(i)为右端点的最大的连续段的左端点,可以解(L_i)直接判断是否可以成功找到这样的连续段.

    那么如何求(L_i)呢,我们可以在(r)增大时对每个(l)维护(max{[l,r]}-min{[l,r]}-(r-l)),则最小值即=0的位置即是连续段的左端点,找到最左边的0就是(L_i)了.(max,min)的部分用单调栈维护即可.

    建树部分复杂度(O(n)),求(L_i)复杂度(O(nlog n)),总时间复杂度(O(n log n))

    Code

    https://www.luogu.com.cn/problem/P4747

    求包含([l,r])的最小连续段.

    找到([l,l],[r,r])在析合树上的lca,若其为析点,那么答案为lca,若其为合点,那么答案是它们对应儿子构成的区间.

    #include <cassert>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <vector>
    #define debug(...) fprintf(stderr,__VA_ARGS__)
    #define lson u<<1,l,mid
    #define rson u<<1|1,mid+1,r
    using namespace std;
    inline char gc() {
    //	return getchar();
    	static char buf[100000],*l=buf,*r=buf;
    	return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
    }
    template<class T> void rd(T &x) {
    	x=0; int f=1,ch=gc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    	while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
    	x*=f;
    } 
    const int maxn=100000+50;
    int n,m,a[maxn];
    namespace rmq {
    	int bit[25],lg2[maxn];
    	int mn[25][maxn],mx[25][maxn];
    	void init() {
    		bit[0]=1;
    		for(int i=1;i<25;++i) bit[i]=bit[i-1]<<1;
    		lg2[0]=-1;
    		for(int i=1;i<=n;++i) lg2[i]=lg2[i>>1]+1;
    		for(int i=1;i<=n;++i) mn[0][i]=mx[0][i]=a[i];
    		for(int k=1;bit[k]<=n;++k) {
    			for(int i=1;i+bit[k]-1<=n;++i) {
    				mn[k][i]=min(mn[k-1][i],mn[k-1][i+bit[k-1]]);
    				mx[k][i]=max(mx[k-1][i],mx[k-1][i+bit[k-1]]);
    			}
    		}
    	}
    	inline int Qmax(int l,int r) {
    		int k=lg2[r-l+1];
    		return max(mx[k][l],mx[k][r-bit[k]+1]);
    	}
    	inline int Qmin(int l,int r) {
    		int k=lg2[r-l+1];
    		return min(mn[k][l],mn[k][r-bit[k]+1]);
    	}
    }
    namespace seg {
    	const int maxnode=maxn<<2;
    	int mn[maxnode],tag[maxnode];
    	inline void change(int u,int d) {
    		mn[u]+=d,tag[u]+=d;
    	}
    	inline void pushdown(int u) {
    		if(tag[u]) {
    			change(u<<1,tag[u]);
    			change(u<<1|1,tag[u]);
    			tag[u]=0;
    		}
    	}
    	inline void pushup(int u) {
    		mn[u]=min(mn[u<<1],mn[u<<1|1]);
    	}
    	void build(int u,int l,int r) {
    		tag[u]=0;
    		if(l==r) {
    			mn[u]=0;
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(lson);
    		build(rson);
    		pushup(u);
    	}
    	void update(int u,int l,int r,int ql,int qr,int qv) {
    		if(l==ql&&r==qr) {
    			change(u,qv);
    			return;
    		}
    		int mid=(l+r)>>1;
    		pushdown(u);
    		if(qr<=mid) update(lson,ql,qr,qv);
    		else if(ql>mid) update(rson,ql,qr,qv);
    		else {
    			update(lson,ql,mid,qv);
    			update(rson,mid+1,qr,qv);
    		}
    		pushup(u);
    	}
    	int query(int u,int l,int r) {
    		if(l==r) return l;
    		int mid=(l+r)>>1;
    		pushdown(u);
    		if(mn[u<<1]==0) return query(lson);
    		else return query(rson);
    	} 
    }
    namespace dct {
    	const int maxnode=maxn<<1;
    	int ncnt;
    	int root;
    	int rnk[maxn];
    	int L[maxnode],R[maxnode],M[maxnode],typ[maxnode];
    	int head[maxnode];
    	int dep[maxnode],parent[maxnode][20];
    	struct edge {
    		int to,nex;
    		edge(int to=0,int nex=0):to(to),nex(nex){}
    	};
    	vector<edge> G;
    	inline void addedge(int u,int v) {
    		G.push_back(edge(v,head[u])),head[u]=G.size()-1;
    	}
    	void dfs(int u) {
    		for(int i=1;i<20;++i) parent[u][i]=parent[parent[u][i-1]][i-1];
    		for(int i=head[u];~i;i=G[i].nex) {
    			int v=G[i].to;
    			dep[v]=dep[u]+1;
    			parent[v][0]=u;
    			dfs(v);
    		}
    	}
    	int jump(int u,int k) {
    		for(int i=19;~i;--i) if(k>>i&1) {
    			u=parent[u][i];
    		}
    		return u;
    	}
    	int lca(int u,int v) {
    		if(dep[u]>dep[v]) swap(u,v);
    		v=jump(v,dep[v]-dep[u]);
    		if(u==v) return u;
    		for(int i=19;~i;--i) {
    			if(parent[u][i]!=parent[v][i]) {
    				u=parent[u][i];
    				v=parent[v][i];
    			}
    		}
    		return parent[u][0];
    	}
     	inline bool judge(int l,int r) {return r-l==rmq::Qmax(l,r)-rmq::Qmin(l,r);}
    	void build() {
    		static int sta[maxnode],sta1[maxn],sta2[maxn]; int top=0,top1=0,top2=0;
    		seg::build(1,1,n);
    		memset(head,-1,sizeof(head));
    		for(int r=1;r<=n;++r) {
    			while(top1&&a[sta1[top1]]<a[r]) {
    				seg::update(1,1,n,sta1[top1-1]+1,sta1[top1],-a[sta1[top1]]);
    				--top1;
    			}
    			sta1[++top1]=r;
    			seg::update(1,1,n,sta1[top1-1]+1,sta1[top1],a[sta1[top1]]);
    			while(top2&&a[sta2[top2]]>a[r]) {
    				seg::update(1,1,n,sta2[top2-1]+1,sta2[top2],a[sta2[top2]]);
    				--top2;
    			}
    			sta2[++top2]=r;
    			seg::update(1,1,n,sta2[top2-1]+1,sta2[top2],-a[sta2[top2]]);
    			int u=++ncnt; L[u]=R[u]=r; rnk[r]=u;
    			int mnl=seg::query(1,1,n);
    			while(top&&L[sta[top]]>=mnl) {
    				if(typ[sta[top]]&&judge(M[sta[top]],r)) {
    					addedge(sta[top],u);
    					R[sta[top]]=r;
    					u=sta[top--];
    				}
    				else if(judge(L[sta[top]],r)) {
    					int v=++ncnt; typ[v]=1;
    					L[v]=L[sta[top]],R[v]=r,M[v]=L[u];
    					addedge(v,sta[top--]),addedge(v,u);
    					u=v;
    				}
    				else {
    					int v=++ncnt; addedge(v,u);
    					do addedge(v,sta[top--]); while(!judge(L[sta[top]],r));
    					L[v]=L[sta[top]],R[v]=r;
    					addedge(v,sta[top--]);
    					u=v;
    				}
    			}
    			sta[++top]=u;
    			seg::update(1,1,n,1,r,-1);
    		}
    		root=sta[top];
    		dfs(root);
    	}
    	inline void sol(int l,int r) {
    		l=rnk[l],r=rnk[r]; 
    		int w=lca(l,r); 
    		if(!typ[w]) printf("%d %d
    ",L[w],R[w]);
    		else {
    			l=jump(l,dep[l]-dep[w]-1);
    			r=jump(r,dep[r]-dep[w]-1);
    			printf("%d %d
    ",L[l],R[r]);
    		}
    	}
    }
    int main() {
    //	freopen("1.in","r",stdin);
    //	freopen("1.out","w",stdout);
    	rd(n);
    	for(int i=1;i<=n;++i) rd(a[i]);
    	rmq::init();
    	dct::build();
    	rd(m);
    	for(int i=1;i<=m;++i) {
    		int x,y; rd(x),rd(y);
    		dct::sol(x,y);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java中的集合类
    Java中的包装类
    Java中的多线程总结(转)
    Java开发中的23种设计模式详解 (转)
    Java中异常处理和设计
    Jmeter的ForEach控制器
    Jmeter事务控制器
    Jmeter下线程顺序启动
    Jmeter跨线程调用参数
    Jmeter使用Python
  • 原文地址:https://www.cnblogs.com/ljzalc1022/p/13277434.html
Copyright © 2020-2023  润新知