zoukankan      html  css  js  c++  java
  • 【树剖求LCA】树剖知识点

    不太优美但是有注释的版本:

    #include<cstdio>
    #include<iostream>
    using namespace std;
    struct edge{
        int to,ne;
    }e[1000005];
    int n,m,s,ecnt,head[500005],dep[500005],siz[500005],son[500005],top[500005],f[500005];
    void add(int x,int y)    //加边
    {
        e[++ecnt].to=y;
        e[ecnt].ne=head[x];
        head[x]=ecnt;
    }
    void dfs1(int x)                             //构造树
    {
        siz[x]=1;                                //假设当前节点仅有一个儿子
        dep[x]=dep[f[x]]+1;                      //当前节点深度=父亲节点深度+1
        for(int i=head[x];i;i=e[i].ne)          //遍历所有的子节点
        {
            int dd=e[i].to;
            if(dd==f[x])continue;               //如果是父节点,则略过
            f[dd]=x;                             //那么确定x是当前节点的父亲
            dfs1(dd);                            //向下遍历
            siz[x]+=siz[dd];                     //遍历完子树之后,加上子树的大小
            if(!son[x]||siz[son[x]]<siz[dd])    //如果x节点重儿子未确定或者重儿子的子树比当前遍历节点的子树小
                son[x]=dd;                       //更新重儿子
        }
    }
    
    void dfs2(int x,int tv)                     //求重链
    {
        top[x]=tv;                               //设置x所在重链顶为tv
        if(son[x])dfs2(son[x],tv);              //如果x有重儿子,那么随着这条重链走
        for(int i=head[x];i;i=e[i].ne)            
        {
            int dd=e[i].to; 
            if(dd==f[x]||dd==son[x])continue;  //如果走到父亲或者走到重儿子(已经走过重儿子,避免重复),那么跳过
            dfs2(dd,dd);                        //开启一条新链,链顶是其本身
        }
    }
    int lca(int x,int y)
    {
    	while(top[x]!=top[y])                              //如果二者不在同一条重链上
        { 
    		if(dep[top[x]] >= dep[top[y]]) x=f[top[x]];   //选择所在重链的顶的深度较大的点向上跳,目的是防止跳过LCA
    		else y=f[top[y]];
    	}
    	return dep[x] < dep[y] ?x :y;                     //当二者在同一条重链上的时候,选择深度较浅的点即为lca
    	
    	
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&s);
        for(int i=1;i<n;++i)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dfs1(s);
        dfs2(s,s);
        for(int i=1;i<=m;++i)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d
    ",lca(x,y));
        }
    }
    

      

    比较优美但是没注释的版本:

    #include<iostream>
    #include<vector>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll INF=99999999;
    const int N = 500010;
    
    int n,m,s;
    
    struct edge{
    	int to,ne;
    	
    }e[N*2];
    
    int top[N],siz[N],son[N],fa[N],dep[N];
    int head[N],ecnt = 1;
    
    void add(int x,int y)
    {
    	e[ecnt].to = y;
    	e[ecnt].ne = head[x];
    	
    	head[x] = ecnt++;
    }
    
    void dfs1(int x)
    {
    	siz[x] = 1;
    	dep[x] = dep[fa[x]] + 1;
    	
    	for(int i = head[x];i;i = e[i].ne){
    		int t = e[i].to;
    		if(t == fa[x]) continue;
    		fa[t] = x;
    		
    		dfs1(t);
    		siz[x] += siz[t];
    		if(!son[x]||siz[son[x]] < siz[t])
    			son[x] = t;
    	}
    }
    
    void dfs2(int x,int tp)
    {
    	top[x] = tp;
    	
    	if(son[x])
    		dfs2(son[x],tp);
    		
    	for(int i = head[x];i;i = e[i].ne){
    		int t = e[i].to;
    		if(t == son[x]||t == fa[x]) continue;
    		
    		dfs2(t,t);
    	}	
    
    }
    
    int lca(int x,int y)
    {
    	while(top[x] != top[y]){
    		if(dep[top[x]] >= dep[top[y]]) x = fa[top[x]];
    		else y = fa[top[y]];
    	}
    	return dep[x] < dep[y] ?x :y;
    }
    int main()
    {
    	
    	scanf("%d%d%d",&n,&m,&s);
    	for(int i = 1;i < n;i++){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		
    		add(x,y);
    		add(y,x);
    	}
    	dfs1(s);
    	dfs2(s,s);
    	
    	for(int i = 1;i <= m;i++){
    		int a,b;
    		scanf("%d%d",&a,&b);
    		
    		printf("%d
    ",lca(a,b));
    	}
    	return 0;
    }
    

    树剖理解容易,需要注意的是题目如果给的是双向边,e数组需要开两倍于边数

      

  • 相关阅读:
    SuperMap房产测绘成果管理平台
    SuperMap产权登记管理平台
    Android adb shell am 的用法(1)
    由浅入深谈Perl中的排序
    Android 内存监测和分析工具
    Android 网络通信
    adb server is out of date. killing...
    引导页使用ViewPager遇到OutofMemoryError的解决方案
    adb logcat 详解
    How to send mail by java mail in Android uiautomator testing?
  • 原文地址:https://www.cnblogs.com/dudujerry/p/10192526.html
Copyright © 2011-2022 走看看