Closest Common Ancestors (Lca,tarjan)

午时刷题,难甚,遂小憩于桌上,惊醒,于梦中有所得,虽大声曰:吾已得tarjan之奥秘!

关于tarjan算法,其实就是一个递归加并查集的应用。

大致代码:

#include<bits/stdc++.h>
using namespace std;
int find(int x){
        ....
}

void  join(int x,int y)
{
    ....
}
void dfs(int x)
{
    int len=v[x].size();
    for(int i=0; i<len; i++)      //遍历x的子节点
    {
        dfs(v[x][i]);           //继续往下推
        join(v[x][i],x);        //将x的所有子节点的祖先都设为x
    }
    vis[x] = true;              //证明x走过了
    for(int i=1; i<=n; i++)         //对每个x循环1~n
        if(vis[i]&&g[x][i])       //如果i已经走过并且要求(x,i)
            ans=find(i);       //lca就是ans
}

由以上代码可以看出,tarjan实际上就是并查集与dfs的结合,其最核心的部分就是dfs那部分

只要理解了dfs()的内容,就能理解tarjan

而对于dfs函数,我们首先就会想到它的特性:不撞南墙不回头。

假如有一颗树,对其dfs,那么首先它会沿着一个分支一直到尽头(如图):

而当走到4这个点时,函数开始执行下列语句:

join(v[x][i],x);        //将x的所有子节点的祖先都设为x //而此时pre[4]=3;pre[3]=3;pre[2]=2;pre[1]=1;

再然后是:

 vis[x] = true;              //证明x走过了
    for(int i=1; i<=n; i++)         //对每个x循环1~n
        if(vis[i]&&g[x][i])       //如果i已经走过并且要求(x,i)
            ans=find(i);       //lca就是ans
}如果存在要求lca[x][i],先看i点是否走过,如果走过,那就只有一种可能(真相只有一个!真実はいつも一つ):

i,k必在两条不同分支上,并且交于某个点x,如果i已经走过了,那么,i所在的这条分支上所有的点都有明确的父子关系,即find(i)==x!代码参上:
#pragma GCC optimize(2)
#include<stdio.h>
#include<string.h>
#include<vector>
#define M 1007
using namespace std;
int g[M][M],in[M],pre[M],cnt[M];
bool vis[M];
vector<int>v[M];
int n,m;
void init()
{
    memset(g,0,sizeof(g));
    memset(in,0,sizeof(in));
    memset(cnt,0,sizeof(cnt));
    memset(vis,false,sizeof(vis));
    for(int i=1; i<=n; i++)
    {
        v[i].clear();
        pre[i]=i;
    }
}
int fond(int x)
{
    return x==pre[x]?x:pre[x]=fond(pre[x]);
}
void join(int x,int y)
{
    int xx=fond(x);
    int yy=fond(y);
    pre[xx]=yy;
}
void dfs(int x)
{
    int len=v[x].size();
    for(int i=0; i<len; i++)      //遍历x的子节点
    {
        dfs(v[x][i]);           //继续往下推
        join(v[x][i],x);        //将x的所有子节点的祖先都设为x
    }
    vis[x] = true;              //证明x走过了
    for(int i=1; i<=n; i++)         //对每个x循环1~n
        if(vis[i]&&g[x][i])       //如果i已经走过并且要求(x,i)
            cnt[fond(i)]+=g[x][i];  //
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        init();
        int a,b,c,root;
        for(int i=1; i<=n; i++)
        {
            scanf("%d:(%d)",&a,&b);
            while(b--)
            {
                scanf(" %d",&c);
                v[a].push_back(c);
                in[c]++;
            }
        }
        scanf("%d",&m);
        getchar();
        while(m--)
        {
            scanf("(%d,%d)",&a,&b);
            getchar();
            g[a][b]++;
            g[b][a]++;
        }
        for(int i=1; i<=n; i++)
            if(!in[i])
            {
                root=i;
                break;
            }
        dfs(root);
        for(int i=1; i<=n; i++)
        {
            if(cnt[i])
                printf("%d\n",i);
        }
    }
    return 0;
}
以上;

原文地址:https://www.cnblogs.com/zjydeoneday/p/lca.html

时间: 2024-11-07 09:17:33

Closest Common Ancestors (Lca,tarjan)的相关文章

POJ 1470 Closest Common Ancestors LCA题解

本题也是找LCA的题目,不过要求多次查询,一般的暴力查询就必然超时了,故此必须使用更高级的方法,这里使用Tarjan算法. 本题处理Tarjan算法,似乎输入处理也挺麻烦的. 注意: 因为查询的数据会极大,故此使用一个数组记录所有查询数据就会超时的.我就载在这里了.查了好久才想到这点.因为我使用了一个vector容器记录了查询数据,故此每次都循环这组这么大的数据,就超时了.----解决办法:使用一个vector<int> quest来记录查询数组,这样每次都只需要循环某节点的邻接查询点就可以了

poj 1470 Closest Common Ancestors 【Tarjan 离线 LCA】

题目:poj 1470 Closest Common Ancestors 题意:给出一个树,一些询问.求LCA的个数. 分析:很简单的模板题目,但是模板不够优秀,一直wa...RE,各种错误一下午,终于发现自己模板的漏洞了. AC代码: #include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; #define N 1010 #

poj 1470 Closest Common Ancestors LCA

题目链接:http://poj.org/problem?id=1470 Write a program that takes as input a rooted tree and a list of pairs of vertices. For each pair (u,v) the program determines the closest common ancestor of u and v in the tree. The closest common ancestor of two n

POJ1470 Closest Common Ancestors 【Tarjan的LCA】

非常裸的模版题,只是Tarjan要好好多拿出来玩味几次 非常有点巧妙呢,tarjan,大概就是当前结点和它儿子结点的羁绊 WA了俩小时,,,原因是,这个题是多数据的(还没告诉你T,用scanf!=EOF来控制结束),更重要的是和这个和Codeforces不一样,Codeforces的多组数据好像会又一次開始程序似的,不用在程序里面写清零,但这个题是多数据用EOF来控制输入的,多数据在一个文件中都一次输进去了,所以要memset btw,加上一点memset代码,多了700B代码... #incl

POJ 1470 Closest Common Ancestors(LCA&amp;RMQ)

题意比较费劲:输入看起来很麻烦.处理括号冒号的时候是用%1s就可以.还有就是注意它有根节点...Q次查询,我是用在线st做的. /************************************************************************* > File Name: 3.cpp > Author: Howe_Young > Mail: [email protected] > Created Time: 2015年10月08日 星期四 19时03分

Poj1470 Closest Common Ancestors LCA

LCA裸题.. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> using namespace std; const int maxn = 1111; int head[maxn]; int len; struct Node { int to; int next; }e[maxn * 2]; int father[maxn];

POJ 1470 Closest Common Ancestors【最近公共祖先LCA】

题目链接:http://poj.org/problem?id=1470 题目大意:给出一棵树,再给出若干组数(a,b),输出节点a和节点b的最近公共祖先(LCA) 就是很裸的LCA,但是我用的是<挑战程序设计竞赛>上的"基于二分搜索的算法求LCA",我看网上用的都是tarjan算法.但是我的代码不知道为什么提交上去 wrong answer,自己想的很多测试数据也都和题解结果一样,不知道错在哪里,所以把代码保存一下,留待以后解决...... 如果读者有什么建议,希望提出来,

POJ 1470 Closest Common Ancestors 【LCA】

任意门:http://poj.org/problem?id=1470 Closest Common Ancestors Time Limit: 2000MS   Memory Limit: 10000K Total Submissions: 22519   Accepted: 7137 Description Write a program that takes as input a rooted tree and a list of pairs of vertices. For each pa

poj----(1470)Closest Common Ancestors(LCA)

Closest Common Ancestors Time Limit: 2000MS   Memory Limit: 10000K Total Submissions: 15446   Accepted: 4944 Description Write a program that takes as input a rooted tree and a list of pairs of vertices. For each pair (u,v) the program determines the