POJ 1330 Nearest Common Ancestors LCA(在线RMQ,离线Tarjan)

链接:http://poj.org/problem?id=1330

题意:只看题目就知道题目是什么意思了,最近公共祖先,求在一棵树上两个节点的最近公共祖先。

思路:求最近公共祖先有两种算法,在线和离线,在线方法是用RMQ求LCA,一句话总结就是在从DFS时,从第一个点到第二个点的最短路径中深度最浅的点就是公共祖先,用RMQ处理,一般问题的最优解决方式的复杂度是O(NlogN)的预处理+N*O(1)的查询。离线方法是Tarjan算法,将所有询问的两个点都记录下来,在DFS过程中不断将每个点自身作为祖先,然后将所有子树遍历结束后,祖先变为它的父节点,在过程中如果遍历到某个节点,同时关于它的询问的另一点已遍历,则此时另一点的祖先就是它们的公共祖先,虽然算法不是很好说明白,不过看代码还是很显而易见的。

代码:

在线算法:RMQ求LCA

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x7fffffff
#define maxn 10005
#define PI acos(-1.0)
#define seed 31//131,1313
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
int stTable [maxn*2][32];
int preLog2[maxn*2];
int depth=0;
int d[maxn*2];
bool vis[maxn];
int bn=0,b[maxn*2]; //深度序列
int f[maxn*2];          //对应深度序列中的结点编号
int p[maxn];             //结点在深度序列中的首位置
int dis[maxn];          //结点到根的距离
int head[maxn];
void st_prepare(int n,int *array)
{
    preLog2[1]=0;
    for(int i=2;i<=n;i++)
    {
        preLog2[i]=preLog2[i-1];
        if((1<<preLog2[i]+1)==i)
        preLog2[i]++;
    }
    for(int i=n-1;i>=0;i--)
    {
        stTable[i][0]=array[i];
        for(int j=1;(i+(1<<j)-1)<n;j++)
        {
            stTable[i][j]=min(stTable[i][j-1],stTable[i+(1<<j-1)][j-1]);
        }
    }
    return ;
}
int query_min(int l,int r)
{
    int len=r-l+1,k=preLog2[len];
    return min(stTable[l][k],stTable[r-(1<<k)+1][k]);
}
int point[maxn];    //记录每个点对应的第一条边的序号
struct Edge
{
    int v;//连接点
    int next;//下一条从此边的出发点发出的边
}edge[maxn*2];
int top;
int init()
{
    memset(vis,0,sizeof(vis));
    memset(point,-1,sizeof(point));
    memset(dis,0,sizeof(dis));
    top=0;
    bn=0;
    depth=0;
}
int add_edge(int u,int v)
{
    edge[top].v=v;
    edge[top].next=point[u];//上一条边的编号
    point[u]=top++;//u点的第一条边编号变成head
}
void dfs(int u,int fa)
{
    int tmp=++depth;
    b[++bn]=tmp; f[tmp]=u; p[u]=bn;
    for (int i=point[u]; i!=-1; i=edge[i].next)
    {
        int v=edge[i].v;
        if (v==fa) continue;
        dis[v]=dis[u]+1;//edge[i].v
        dfs(v,u);
        b[++bn]=tmp;
    }
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        int tot,root=0,aa,bb;
        scanf("%d",&tot);
        for(int i=1;i<=tot;i++)
            head[i]=i;
        for(int i=1;i<=tot-1;i++)
        {
            scanf("%d%d",&aa,&bb);
            add_edge(aa,bb);
            add_edge(bb,aa);
            head[bb]=aa;
        }
        for(int i=1;i<=tot;i++)
        {
            if(head[i]==i)
            {
                root=i;
                break;
            }
        }
        dfs(root,root);
        st_prepare(tot*2-1,b);
        scanf("%d%d",&aa,&bb);
        if(p[aa]<p[bb])
            cout<<f[query_min(p[aa],p[bb])]<<endl;
        else cout<<f[query_min(p[bb],p[aa])]<<endl;
    }
}

离线算法:Tarjan算法

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x7fffffff
#define maxn 10005
#define PI acos(-1.0)
#define seed 31//131,1313
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
int pre[maxn],point[maxn],point2[maxn];
bool vis[maxn];
struct Edge
{
    int v;//连接点
    int next;//下一条从此边的出发点发出的边
} edge[maxn*2];
struct Query
{
    int v;
    int w;
    int next;
} query[maxn];
int top,top2;
int init()
{
    memset(vis,0,sizeof(vis));
    memset(point,-1,sizeof(point));
    memset(point2,-1,sizeof(point2));
    top=0;
    top2=0;
}
int add_edge(int u,int v)
{
    edge[top].v=v;
    edge[top].next=point[u];//上一条边的编号
    point[u]=top++;//u点的第一条边编号变成head
}
int findset(int x) //并查集
{
    if(x!=pre[x])
    {
        pre[x]=findset(pre[x]); //路径压缩
    }
    return pre[x];
}
int add_query(int u,int v)
{
    query[top2].v=v;
    query[top2].w=-1;
    query[top2].next=point2[u];//上一条边的编号
    point2[u]=top2++;//u点的第一条边编号变成head
    query[top2].v=u;
    query[top2].w=-1;
    query[top2].next=point2[v];//上一条边的编号
    point2[v]=top2++;//u点的第一条边编号变成head
}
int lca(int u,int f) //当前节点,父节点
{
    pre[u]=u;  //设立当前节点的集合
    for(int i=point[u]; i!=-1; i=edge[i].next)
    {
        if(edge[i].v==f)
            continue;
        lca(edge[i].v,u);  //搜索子树
        pre[edge[i].v]=u; //合并子树
    }
    vis[u]=1; //以u点为集合的点搜索完毕
    for(int i=point2[u]; i!=-1; i=query[i].next)
    {
        if(vis[query[i].v]==1)
            query[i].w=findset(query[i].v);
    }
    return 0;
}
int main()
{
    int root[maxn];
    int T;
    scanf("%d",&T);
    for(int ii=0; ii<T; ii++)
    {
        init();
        int tot,r=-1,a,b;
        scanf("%d",&tot);
        for(int i=1; i<=tot; i++)
            root[i]=i;
        for(int i=0; i<tot-1; i++)
        {
            scanf("%d%d",&a,&b);
            add_edge(a,b);
            add_edge(b,a);
            root[b]=a;
        }
        for(int i=1; i<=tot; i++)
            if(root[i]==i)
                r=i;//树的根
        scanf("%d%d",&a,&b);
        add_query(a,b);
        lca(r,r);
        //cout<<top2<<endl;
        for(int i=0;i<top2;i++)
        {
            if(query[i].w!=-1)
            printf("%d\n",query[i].w);
        }

    }
    return 0;
}

P.S.其实这道题只有一次询问,所以先DFS第一个点,记录路径,然后DFS找到第二个点,记录路径,两条路径的第一个重合点也是公共祖先。

POJ 1330 Nearest Common Ancestors LCA(在线RMQ,离线Tarjan)

时间: 2024-10-03 06:55:10

POJ 1330 Nearest Common Ancestors LCA(在线RMQ,离线Tarjan)的相关文章

poj 1330 Nearest Common Ancestors lca 在线rmq

Nearest Common Ancestors Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:  In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree.

POJ 1330 Nearest Common Ancestors LCA题解

本题是一个多叉树,然后求两点的最近公共单亲节点. 就是典型的LCA问题.这是一个很多解法的,而且被研究的很透彻的问题. 原始的解法:从根节点往下搜索,若果搜索到两个节点分别在一个节点的两边,那么这个点就是最近公共单亲节点了. Trajan离线算法:首次找到两个节点的时候,如果记录了他们的最低单亲节点,那么答案就是这个最低的单亲节点了. 问题是如何有效记录这个最低单亲节点,并有效根据遍历的情况更新,这就是利用Union Find(并查集)记录已经找到的节点,并及时更新最新访问的节点的当前最低单亲节

POJ 1330 Nearest Common Ancestors(LCA模板)

给定一棵树求任意两个节点的公共祖先 tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断.如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果访问过的话,那么它的最近公共祖先就是当前节点祖先. 下面是tarjan离线模板: #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn =

POJ 1330 Nearest Common Ancestors(最近公共祖先 Tarjan离线)

题目链接:http://poj.org/problem?id=1330 题目: Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:  In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the ro

POJ 1330 Nearest Common Ancestors LCA

题目链接: http://poj.org/problem?id=1330 题意: 给你一颗有根树,最后输入一对数(a,b),叫你求a和b的公共祖先. 裸的lca,数据也很小,拿来练手不错. 题解: 1.tarjan_lca,离线,线性时间复杂度 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6

POJ 1330 Nearest Common Ancestors 倍增算法的LCA

POJ 1330 Nearest Common Ancestors 题意:最近公共祖先的裸题 思路:LCA和ST我们已经很熟悉了,但是这里的f[i][j]却有相似却又不同的含义.f[i][j]表示i节点的第2j个父亲是多少   这个代码不是我的,转自 邝斌博客 1 /* *********************************************** 2 Author :kuangbin 3 Created Time :2013-9-5 9:45:17 4 File Name :F

POJ - 1330 Nearest Common Ancestors(基础LCA)

POJ - 1330 Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000KB   64bit IO Format: %lld & %llu Submit Status Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:  In t

[POJ 1330] Nearest Common Ancestors (朴素方法)

POJ 1330: Nearest Common Ancestors Time Limit: 1000ms Memory Limit: 32Mb Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:  In the figure, each node is labeled with an integer fro

POJ 1330 Nearest Common Ancestors(树)

Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 17628   Accepted: 9335 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each