zoukankan      html  css  js  c++  java
  • 最近公共祖先(一道题目)

    Description
      
    Description
    Description  
      
    Sample Input
      
       15 5
      
       1 2 3 4 5 6 7 8 9 10 11 12 13 14
      
       1 1 2 2 3 3 4 4 5 5 6 6 7 7
      
       1 2
      
       8 11
      
       5 8
      
       8 15
      
       4 6
        
    Sample Output
      
       1
      
       5
      
       4
      
       7
      
       3
      
    HINT
      
    Hint
    Hint
      
      
      
    Solution
      
       题目求的”最近公共祖先“,实际上是所有公共祖先中编号最大的那一个......
      
       看看怎么把两棵树联系起来。
      
       要同时维护询问的两个点的祖先的并集比较困难,考虑能不能离线简化一下问题。
      
       我们dfs遍历A树,每走到A树的一个点u,回答所有形如(u,v)的询问,其中v是B树上的点。
      
       注意到遍历到u时,根节点到u的路径构成了u的祖先集合F_u。于是我们就可以转化一下问题,对于每个询问,相当于询问在F_u中,是v在B树中的祖先的最大值。
      
       预处理出A树中每一个点在B树中的dfn出序和入序,以此刻画出A树的每一个点在B树中对应哪一棵子树。在A树中每遍历到一个点,就给B树中的相应子树中的所有点更新最大值。这些操作用线段树维护B树的dfn序实现。此时问题变得非常简单,此时只需要回答v在线段树中相应位置的值即可。
      
       注意到A树的dfs有回溯操作,所以用主席树解决就可以了。主席树是区间修改、单点查询,注意标记永久化。
      
       这题的关键就在于如何转化询问,使得问题变得易于维护。
      
      

    #include <cstdio>
    #include <vector>
    #define pb push_back
    #define mp make_pair
    using namespace std;
    typedef pair<int,int> pii;
    const int N=200005;
    int n,m,out[N];
    int ha[N],hb[N],tot;
    int dfnb[N][2],dfntm;
    vector<pii> q[N];
    struct Edge{int v,next;}e[N*2];
    inline void addEdge(int u,int v,int *h){e[++tot]=(Edge){v,h[u]};h[u]=tot;}
    namespace CEG{/*{{{*/
    	const int S=N*2*18;
    	int rt[N],sz,ch[S][2],maxs[S];
    	inline int copy(int u){
    		int v=++sz;
    		ch[v][0]=ch[u][0]; ch[v][1]=ch[u][1];
    		maxs[v]=maxs[u];
    		return v;
    	}
    	void insert(int u,int &v,int l,int r,int L,int R,int x){
    		v=copy(u);
    		if(L<=l&&r<=R){
    			maxs[v]=max(maxs[v],x);
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(R<=mid) insert(ch[u][0],ch[v][0],l,mid,L,R,x);
    		else if(mid<L) insert(ch[u][1],ch[v][1],mid+1,r,L,R,x);
    		else{
    			insert(ch[u][0],ch[v][0],l,mid,L,mid,x);
    			insert(ch[u][1],ch[v][1],mid+1,r,mid+1,R,x);
    		}
    	}
    	int query(int u,int l,int r,int pos,int now){
    		now=max(now,maxs[u]);
    		if(l==r) return now;
    		int mid=(l+r)>>1;
    		if(pos<=mid) return query(ch[u][0],l,mid,pos,now);
    		else		 return query(ch[u][1],mid+1,r,pos,now);
    	}
    }/*}}}*/
    void dfsB(int u,int fa){
    	dfnb[u][0]=++dfntm;
    	for(int i=hb[u],v;i;i=e[i].next)
    		if((v=e[i].v)!=fa)
    			dfsB(v,u);
    	dfnb[u][1]=dfntm;
    }
    void dfsA(int u,int fa){
    	CEG::insert(CEG::rt[fa],CEG::rt[u],1,n,dfnb[u][0],dfnb[u][1],u);
    	for(int i=0,sz=q[u].size();i<sz;i++){
    		int v=q[u][i].first,qid=q[u][i].second;
    		out[qid]=CEG::query(CEG::rt[u],1,n,dfnb[v][0],0);
    	}
    	for(int i=ha[u],v;i;i=e[i].next)
    		if((v=e[i].v)!=fa)
    			dfsA(v,u);
    }
    int main(){
    	freopen("input.in","r",stdin);
    	scanf("%d%d",&n,&m);
    	for(int v=2;v<=n;v++){
    		int u;
    		scanf("%d",&u);
    		addEdge(u,v,ha);
    	}
    	for(int v=2;v<=n;v++){
    		int u;
    		scanf("%d",&u);
    		addEdge(u,v,hb);
    	}
    	for(int i=1;i<=m;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		q[x].pb(mp(y,i));
    	}
    	dfsB(1,0);
    	dfsA(1,0);
    	for(int i=1;i<=m;i++) printf("%d
    ",out[i]);
    	return 0;
    }
    
  • 相关阅读:
    PHP 函数大集合
    PHP 单词集合
    PHP 常用函数集合
    Linux 服务器中搭建环境
    windows下cmd中命令操作
    TP中的AJAX返回ajaxReturn()
    PHP面试题
    CI表单验证
    CI数据库操作_查询构造器类
    react 的核心思想 【声名式】Declarative 的理解
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/8905954.html
Copyright © 2011-2022 走看看