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\n",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\n",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\n");
            else
                if(out[i].s==ans[i])
                      printf("1\n");
            else if(out[i].e==ans[i])
                printf("%d\n",dis[out[i].s]-dis[ans[i]]);
            else
            printf("%d\n",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\n");
            else
                printf("%d\n",ans[i]);
        }
    }
    return 0;
}
/*本题给的是一个森林 而不是一颗树,由于在加入边的时候,我们让2个方向都能走 这样就
形成了一个强连通的快,  对于这个快来说,不管从快上那点出发 都可以遍历这个快上的所
有的点,且相对距离是一样的*/
时间: 2024-10-13 05:40:03

LCA-tarjan understand 2的相关文章

ZOJ_3195_Design the city(LCA+tarjan)

Design the city Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu Submit Status Description Cerror is the mayor of city HangZhou. As you may know, the traffic system of this city is so terrible, that there are traffic jams eve

POJ_1986_Distance Queries(LCA+tarjan)

Distance Queries Time Limit: 2000MS   Memory Limit: 30000KB   64bit IO Format: %I64d & %I64u Submit Status Description Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore w

HDU_2586 &amp;&amp; HDU_2874 (LCA+tarjan)

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8541    Accepted Submission(s): 2997 Problem Description There are n houses in the village and some bidirectional roads connecting

最近公共祖先LCA(Tarjan算法)的思考和算法实现——转载自Vendetta Blogs

最近公共祖先LCA(Tarjan算法)的思考和算法实现 LCA 最近公共祖先 Tarjan(离线)算法的基本思路及其算法实现 小广告:METO CODE 安溪一中信息学在线评测系统(OJ) //由于这是第一篇博客..有点瑕疵...比如我把false写成了flase...看的时候注意一下! //还有...这篇字比较多 比较杂....毕竟是第一次嘛 将就将就 后面会重新改!!! 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先

hihoCoder #1067 : 最近公共祖先&#183;二 [ 离线LCA tarjan ]

传送门: #1067 : 最近公共祖先·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho用非常拙劣——或者说粗糙的手段山寨出了一个神奇的网站,这个网站可以计算出某两个人的所有共同祖先中辈分最低的一个是谁.远在美国的他们利用了一些奇妙的技术获得了国内许多人的相关信息,并且搭建了一个小小的网站来应付来自四面八方的请求. 但正如我们所能想象到的……这样一个简单的算法并不能支撑住非常大的访问量,所以摆在小Hi和小Ho面前的无非两种选择: 其一是

【LCA/tarjan】POJ1470-Closest Common Ancestors

[题意] 给出一棵树和多组查询,求以每个节点为LCA的查询数有多少? [错误点] ①读入的时候,注意它的空格是随意的呀!一开始不知道怎么弄,后来看了DISCUSS区大神的话: 询问部分输入: scanf("%d",&m); for(int i=0;i<m;i++){ scanf(" (%d %d)",&a,&b); } 注意scanf(" 这里有一个空格 ②多组数据啊!注意这句话:The input file contents

hdu-2874 Connections between cities(lca+tarjan+并查集)

题目链接: Connections between cities Time Limit: 10000/5000 MS (Java/Others)     Memory Limit: 32768/32768 K (Java/Others) Problem Description After World War X, a lot of cities have been seriously damaged, and we need to rebuild those cities. However, s

最近公共祖先 LCA Tarjan算法

来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html 对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点. 0 | 1 /   \ 2      3 比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点. 在求解最近公共祖先为问

HDU 2586 How far away ? (离线LCA Tarjan算法模板)

How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 6422    Accepted Submission(s): 2411 Problem Description There are n houses in the village and some bidirectional roads connecting

我对最近公共祖先LCA(Tarjan算法)的理解

LCA 最近公共祖先 Tarjan(离线)算法的基本思路及我个人理解 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖先节点,而最近公共祖先,就是两个节点在这棵树上深度最大的公共的祖先节点. 换句话说,就是两个点在这棵树上距离最近的公共祖先节点. 所以LCA主要是用来处理当两个点仅有唯一一条确定的最短路径时的路径. 有人可能会问:那他本身或者其父亲节点是否可以作为祖先节点呢? 答案是肯定的,很简单,按照人的亲戚观念来说,你的父亲也是你的祖先,而