• LCA-tarjan understand 2


    下面是一个最基础的LCA题目    http://poj.org/problem?id=1330

    赤裸裸的 题意 输入cas 后  有cas组数据 输入 n   再输入n-1 条边    之后输入x y  问x y的最近公共祖先是什么

    #include<stdio.h>
    #include<vector>
    #include<string.h>
    using namespace std;
    #define Size 11111  //节点个数
    
    vector<int> node[Size],que[Size];
    int n,pare[Size],anse[Size],in[Size],rank[Size];
    
    int vis[Size];
    void init()
    {
        int i;
        for(i=1;i<=n;i++)
        {
            node[i].clear();
            que[i].clear();
            rank[i]=1;
            pare[i]=i;/// 
        }
        memset(vis,0,sizeof(vis));
        memset(in,0,sizeof(in));
        memset(anse,0,sizeof(anse));
         
    }
    
    int find(int nd)//并查集操作  不解释
    {
        return pare[nd]==nd?nd:pare[nd]=find(pare[nd]);
    }
    int Union(int nd1,int nd2)//并查集操作  不解释
    {
        int a=find(nd1);
        int b=find(nd2);
        if(a==b) return 0;
        else if(rank[a]<=rank[b])
        {
            pare[a]=b;
            rank[b]+=rank[a];
        }
        else 
        {
            pare[b]=a;
            rank[a]+=rank[b];
        }
        return 1;
    
    }
    
    void LCA(int root)
    {
        int i,sz;
        anse[root]=root;//首先自成一个集合
        sz=node[root].size();
        for(i=0;i<sz;i++)
        {
               LCA(node[root][i]);//递归子树
               Union(root,node[root][i]);//将子树和root并到一块 
             anse[find(node[root][i])]=root;//修改子树的祖先也指向root
        }
        vis[root]=1;
        sz=que[root].size();
        for(i=0;i<sz;i++)
        {
                if(vis[que[root][i]])
                {
                    printf("%d
    ",anse[find(que[root][i])]);///root和que[root][i]所表示的值的最近公共祖先
                    return ;
                }
        }
         return ;
    }
    
    int main()
    {
        int cas,i;
        scanf("%d",&cas);
        while(cas--)
        {
            int s,e;
            scanf("%d",&n);
            init();
            for(i=0;i<n-1;i++)
            {
                scanf("%d %d",&s,&e);
                if(s!=e)
                {
                    node[s].push_back(e);
                   // node[e].push_back(s);
                    in[e]++;
                }
            }
            scanf("%d %d",&s,&e);
            que[s].push_back(e);
            que[e].push_back(s);
            for(i=1;i<=n;i++)  if(in[i]==0) break;//寻找根节点
        //    printf("root=%d
    ",i);
            LCA(i);
        }
        return 0;
    }

     之后来个加强版

    http://acm.hdu.edu.cn/showproblem.php?pid=4547  CD操作  hdu4547

    思路:

      求出a和b的最近公共祖先,然后分4种情况讨论

      ①. a和b有一个公共祖先c,则用 c时间戳-a的时间戳+1(1步可以直接从c到b)

      ②. a是b的祖先,则只用1步就可以到达b点

      ③. b是a的祖先,则用a的时间戳-b的时间戳

      ④. a和b是同一个点,则答案是0

    参考  http://www.cnblogs.com/Griselda/archive/2013/06/05/3119265.html

    #include<stdio.h>
    #include<vector>
    #include<string.h>
    #include<map>
    #include<math.h>
    #include<string>
    using namespace std;
    #define Size 111111  //节点个数
    struct Query
    {
        int nd,id;
    }temp;
    struct out
    {
        int s,e;
    }out[Size];
    vector<int> node[Size];
    vector<struct Query>que[Size];
    int n,m,pare[Size],ance[Size],in[Size],rank[Size],dis[Size],ans[Size],vis[Size];
    map<string,int>mp;
    void init()
    {
        int i;
        for(i=1;i<=n;i++)
        {
            node[i].clear();
            que[i].clear();
            rank[i]=1;
            pare[i]=i;///
        }
        memset(vis,0,sizeof(vis));
        memset(in,0,sizeof(in));
        memset(ance,0,sizeof(ance));
        memset(dis,0,sizeof(dis));
        mp.clear();
    }
    int aabs(int aa)
    {
         if(aa>0) return aa;
         else return -aa;
    }
    int find(int nd)//并查集操作  不解释
    {
        return pare[nd]==nd?nd:pare[nd]=find(pare[nd]);
    }
    int Union(int nd1,int nd2)//并查集操作  不解释
    {
        int a=find(nd1);
        int b=find(nd2);
        if(a==b) return 0;
        else if(rank[a]<=rank[b])
        {
            pare[a]=b;
            rank[b]+=rank[a];
        }
        else
        {
            pare[b]=a;
            rank[a]+=rank[b];
        }
        return 1;
    }
    void LCA(int root,int num)
    {
        int i,sz;
        ance[root]=root;//首先自成一个集合
        dis[root]=num;
        sz=node[root].size();
        for(i=0;i<sz;i++)
        {
               LCA(node[root][i],num+1);//递归子树
               Union(root,node[root][i]);//将子树和root并到一块
             ance[find(node[root][i])]=root;//修改子树的祖先也指向root
        }
        vis[root]=1;
        sz=que[root].size();
        for(i=0;i<sz;i++)
        {
            int nd1,nd2,idx,ancestor;
            nd1=root;nd2=que[root][i].nd;idx=que[root][i].id;
                if(vis[nd2])
                {
                      ans[idx]=ance[find(nd2)];
                }
        }
         return ;
    }
    
    int main()
    {
        int cas,i;
        scanf("%d",&cas);
        while(cas--)
        {
            char  ss[100],ee[100];
            int s,e,cnt=1;
            scanf("%d %d",&n,&m);
            init();
            for(i=0;i<n-1;i++)
            {
                scanf("%s %s",ee,ss);
                if(mp.find(ss)==mp.end())
                {
                     s=cnt;mp[ss]=cnt++;
                }
                else s=mp[ss];
                if(mp.find(ee)==mp.end())
                {
                    e=cnt;mp[ee]=cnt++;
                }
                else  e=mp[ee];
                if(s!=e)
                {
                    node[s].push_back(e);
                    in[e]++;
                }
            }
            for(i=0;i<m;i++)
            {
               scanf("%s %s",ss,ee);
               s=mp[ss];e=mp[ee];
               out[i].s=s;out[i].e=e;
               temp.nd=e;temp.id=i;
               que[s].push_back(temp);
               temp.nd=s;temp.id=i;
               que[e].push_back(temp);
            }
            for(i=1;i<=n;i++)  if(in[i]==0) break;//寻找根节点
            LCA(i,0);
            for(i=0;i<m;i++)
            {
                if(out[i].s==out[i].e)
                    printf("0
    ");
                else
                    if(out[i].s==ans[i])
                          printf("1
    ");
                else if(out[i].e==ans[i])
                    printf("%d
    ",dis[out[i].s]-dis[ans[i]]);
                else
                printf("%d
    ",dis[out[i].s]-dis[ans[i]]+1);
            }
        }
        return 0;
    }

    hdu   2874

    http://acm.hdu.edu.cn/showproblem.PHP?pid=2874

    题目大意: 给你一个n个节点m条边的森林,再给定q个查询,每次查询森林里两个点的最近距离。n ,m <= 10000,q <= 100万

    本题和标准的LCA模板应用有了不小的区别   却可以让人更加透彻的看清LCA的思路  而且本题没有必要去求出公共祖先

    #include<stdio.h>
    #include<string.h>
    #include<vector>
    using namespace std;
    #define Size  11111
    struct Edge
    {
        int y,val;
    }temp;
    struct Query
    {
        int y,id;
    }mid;
    int pare[Size],ance[Size],vis[Size],dis[Size],rank[Size],ans[1000000+100],n,m,c,tree[Size];
    vector<struct Query>que[Size];
    vector<struct Edge>node[Size];
    void init()
    {
        int i;
        for(i=0;i<=n;i++)
        {
            vis[i]=0;
            pare[i]=i;
            dis[i]=0;
            rank[i]=1;
            que[i].clear();
            node[i].clear();
        }
        memset(ans,-1,sizeof(ans));
    }
    int find(int x)
    {
        return pare[x]==x?x:pare[x]=find(pare[x]);
    }
    /*
    void Union(int x,int y)
    {
    x=find(x);
    y=find(y);
    if(x!=y)
    {
    if(rank[x]>rank[y])
    {
    rank[x]+=rank[y];
    pare[y]=x;
    }
    else
    {
    rank[y]+=rank[x];
    pare[x]=y;
    }
    }
    }
    */
    void LCA(int root,int d,int k)//k表示是以第k个点作为根的树
    {
        int i,sz,nd1,nd2;
        vis[root]=1; //已经遍历过的点 要标记一下 不要
        tree[root]=k;dis[root]=d;
        // ance[root]=root;
        sz=node[root].size();
        for(i=0;i<sz;i++)
        {
            nd2=node[root][i].y;
            if(!vis[nd2])
            {
                LCA(nd2,d+node[root][i].val,k);
                // Union(node[root][i].y,root);//用带rank的幷查集操作答案不对 不知道why
                int w=find(nd2),m=find(root);
                if(w!=m)
                {
                   pare[w]=m;//这样才对
                }
                //ance[find(node[root][i].y)]=root;
            }
        }
        sz=que[root].size();
        for(i=0;i<sz;i++)
        {
            nd1=root;
            nd2=que[root][i].y;
            if(vis[nd2]&&tree[nd1]==tree[nd2])//如果 nd1 nd2 的跟是同一个点 则是同一棵树上的
            {
                ans[que[root][i].id]=dis[nd1]+dis[nd2]-2*dis[find(nd2)];
            }
        }
    }
    int main()
    {
        int i,j,x,y,val;
        while(scanf("%d %d %d",&n,&m,&c)!=EOF)
        {
            init();
            for(i=0;i<m;i++)
            {
                scanf("%d %d %d",&x,&y,&val);
                if(x!=y)
                {
                    temp.y=y;temp.val=val;
                    node[x].push_back(temp);
                    temp.y=x;
                    node[y].push_back(temp);//路是2个方向都可以通行的
                }
            }
            for(i=0;i<c;i++)
            {
                scanf("%d %d",&x,&y);
                mid.id=i;
                mid.y=y;
                que[x].push_back(mid);
                mid.y=x;
                que[y].push_back(mid);
            }
            for(i=1;i<=n;i++)
            {
                LCA(i,0,i);//以每一个节点作为根节点去深度搜索  找出每个点作为根的所有最近公共祖先
            }
            for(i=0;i<c;i++)
            {
                if(ans[i]==-1)
                    printf("Not connected
    ");
                else
                    printf("%d
    ",ans[i]);
            }
        }
        return 0;
    }
    /*本题给的是一个森林 而不是一颗树,由于在加入边的时候,我们让2个方向都能走 这样就
    形成了一个强连通的快,  对于这个快来说,不管从快上那点出发 都可以遍历这个快上的所
    有的点,且相对距离是一样的*/
  • 相关阅读:
    圆上的整点
    学习笔记:用线性筛算不太常见的函数
    解题报告: luogu P1972
    解题报告: luogu P3907
    替罪羊树详解
    解题报告:luogu P2787
    解题报告:luogu P4170
    解题报告:luogu P4933
    10、.运维就是一场没有硝烟的战争
    九、模板层(三)
  • 原文地址:https://www.cnblogs.com/Aragaki/p/7355666.html
Copyright © 2020-2023  润新知