POJ-1330&HDU-2586 最近公共祖先(不带权+带权)树剖式写法求LCA

其实敲树剖敲多了就会手敲,然后就发现其实树剖也是可以求LCA的,根据树剖的经验,我们两遍dfs后关于询问l,r的情况我们就开始跳链,当l,r处于同一个链的时候返回深度较小的那个点就好了,这里给个例题:

题目链接: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 root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is.

For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest common ancestor of y and z is y.

Write a program that finds the nearest common ancestor of two distinct nodes in a tree.

Input

The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,..., N. Each of the next N -1 lines contains a pair of integers that represent an edge --the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers whose nearest common ancestor is to be computed.

Output

Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.

Sample Input

2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5

Sample Output

4
3

其题目大意是给你T组数据,n,n-1条边,其中第一个点是第二个点的父节点,最后询问a,b的最近公共祖先那么我们先找到根节点,即没有父节点的那个点就是了,然后走一波树剖的准备过程。。。以下是树剖式AC代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mac=1e4+10;

struct node
{
    int to,next;
}eg[mac<<1];
int head[mac],num=0,top[mac],son[mac],dson[mac];
int father[mac],d[mac],cnt=0,f[mac];

void add(int u,int v)
{
    eg[++num]=node{v,head[u]};
    head[u]=num;
}

void dfs2(int x,int tp)
{
    top[x]=tp;
    if (!dson[x]) return;
    dfs2(dson[x],tp);
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==father[x] || v==dson[x]) continue;
        dfs2(v,v);
    }
}

void dfs1(int x,int fa,int dp)
{
    d[x]=dp;father[x]=fa;son[x]=1;
    int maxson=-1;
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==fa) continue;
        dfs1(v,x,dp+1);
        son[x]+=son[v];
        if (son[v]>maxson) dson[x]=v,maxson=son[v];
    }
}

int lca(int l,int r)
{
    while (top[l]!=top[r]){
        if (d[top[l]]<d[top[r]]) swap(l,r);
        l=father[top[l]];
    }
    if (d[l]<d[r]) return l;
    return r;
}

void init()
{
        memset(head,-1,sizeof head);
        num=0;cnt=0;
        memset(f,0,sizeof f);
        memset(son,0,sizeof son);
        memset(dson,0,sizeof dson);
        memset(d,0,sizeof d);
        memset(top,0,sizeof top);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int t;
    scanf ("%d",&t);
    while (t--){
        int n;
        scanf ("%d",&n);
        init();
        for (int i=1; i<=n-1; i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);f[v]=u;
        }
        int rt;
        for (int i=1; i<=n; i++) if (!f[i]) {rt=i;break;}
        dfs1(rt,0,1);
        dfs2(rt,rt);
        int l,r;
        scanf("%d%d",&l,&r);
        printf ("%d\n",lca(l,r));
    }

    return 0;
}

接下来就是带权的情况了,实际上我们只需要多加一个dfs就好了,这个dfs求每个点到根节点的距离,那么我们求一波LCA,设该点为p,后用dis[l]+dis[r]-dis[p]*2就完事了。

给个例题:

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586

Problem Description

There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can‘t visit a place twice) between every two houses. Yout task is to answer all these curious people.

Input

First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.

Output

For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.

Sample Input

2

3 2

1 2 10

3 1 15

1 2

2 3

2 2

1 2 100

1 2

2 1

Sample Output

10

25

100

100

题目大意:T组数据,每组n,q,接下来n-1行,为u,v,w,接下来q行询问a到b的最短距离。

以下是AC代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int mac=4e4+10;

struct node
{
    int to,next,w;
}eg[mac<<1];
int head[mac],num=0,top[mac],son[mac],dson[mac];
int father[mac],d[mac],cnt=0,dis[mac];

void add(int u,int v,int w)
{
    eg[++num]=node{v,head[u],w};
    head[u]=num;
}

void dfs2(int x,int tp)
{
    top[x]=tp;
    if (!dson[x]) return;
    dfs2(dson[x],tp);
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==father[x] || v==dson[x]) continue;
        dfs2(v,v);
    }
}

void dfs1(int x,int fa,int dp)
{
    d[x]=dp;father[x]=fa;son[x]=1;
    int maxson=-1;
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==fa) continue;
        dfs1(v,x,dp+1);
        son[x]+=son[v];
        if (son[v]>maxson) dson[x]=v,maxson=son[v];
    }
}

void dfs(int x)
{
    for (int i=head[x]; i!=-1; i=eg[i].next){
        int v=eg[i].to;
        if (v==father[x]) continue;
        dis[v]=dis[x]+eg[i].w;
        dfs(v);
    }
}

int lca(int l,int r)
{
    while (top[l]!=top[r]){
        if (d[top[l]]<d[top[r]]) swap(l,r);
        l=father[top[l]];
    }
    if (d[l]<d[r]) return l;
    return r;
}

void init()
{
        memset(head,-1,sizeof head);
        num=0;cnt=0;
        memset(son,0,sizeof son);
        memset(dson,0,sizeof dson);
        memset(d,0,sizeof d);
        memset(top,0,sizeof top);
}

int main()
{
    //freopen("in.txt","r",stdin);
    int t;
    scanf ("%d",&t);
    while (t--){
        int n,q;
        scanf ("%d%d",&n,&q);
        init();
        for (int i=1; i<=n-1; i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);add(v,u,w);
        }
        int rt=1;
        dfs1(rt,0,1);
        dfs2(rt,rt);
        dfs(rt);
        int l,r;
        for (int i=1; i<=q; i++){
            scanf("%d%d",&l,&r);
            int anc=lca(l,r);
            printf("%d\n",dis[l]+dis[r]-dis[anc]*2);
        }
    }

    return 0;
}

原文地址:https://www.cnblogs.com/lonely-wind-/p/12196144.html

时间: 2024-08-03 20:45:34

POJ-1330&HDU-2586 最近公共祖先(不带权+带权)树剖式写法求LCA的相关文章

HDU 2586 + HDU 4912 最近公共祖先

先给个LCA模板 HDU 1330(LCA模板) #include <cstdio> #include <cstring> #define N 40005 struct Edge{ int x,y,d,ne; }; Edge e[N*2],e2[N*2]; int be[N],be2[N],all,all2,n,m; bool vis[N]; int fa[N]; int ancestor[N][3]; int dis[N]; void add(int x, int y, int

hdu - 2586 How far away ?(最短路共同祖先问题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 最近公共祖先问题~~ 题目大意:一个村子里有n个房子,这n个房子用n-1条路连接起来,接下了有m次询问,每次询问两个房子a,b之间的距离是多少. 很明显的最近公共祖先问题,先建一棵树,然后求出每一点i到树根的距离dis[i],然后每次询问a,b之间的距离=dis[a]+dis[b]-2*dis[LCA(a,b)]; LCA(a,b)即是a,b的最近公共祖先.. 关于最近公共祖先,给大家推荐一个

求解二叉查找树中的最低公共祖先结点

一,问题描述 请构造一棵二叉查找树,并给定两个结点,请找出这两个结点的最低公共祖先结点. 这里假设二叉查找树中的结点的权值存储是整型数字(见代码中的BinaryNode内部类),最低公共祖先结点如下:结点5 和 结点12 的最低公共祖先结点是结点10 二,实现思路 假设给定的两个结点的权值分别为 node1 和 node2 如果根的权值处于 node1 和 node2 之间,则根就是它们的最低公共祖先结点 如果根的权值比 node1 和 node2 都大,则它们的最低公共祖先结点在根的左子树中

lca(最近公共祖先(在线)) 倍增法详解

转自大佬博客 : https://blog.csdn.net/lw277232240/article/details/72870644 描述:倍增法用于很多算法当中,通过字面意思来理解 LCA是啥呢  在一棵树当中 lca表示的是两个节点最近公共祖先, 大家看这课树哈节点5 ,3的lca就是1,13和11的LCA就是6.节点8,12的lca就是8,那么我们如何通过被增来实现LCA呢. 首先请大家认真看下面的分析. depth[x],表示x节点的深度. 大家看下这个数组 grand[x][i] ,

LCA(最近公共祖先)--tarjan离线算法 hdu 2586

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

LCA 算法学习 (最近公共祖先)poj 1330

poj1330 在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点.这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共祖先只有一个.时间复杂度为O(n+q

HDU 2586 How far away ?(LCA模板 近期公共祖先啊)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2586 Problem Description There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house

【POJ 1330】Nearest Common Ancestors(最近公共祖先)

传送门:http://poj.org/problem?id=1330 题意:很裸的最近公共祖先,看题就知道…模板题. 代码: /* *********************************************** Author :Torrance_ZHANG Created Time :2016/4/29 22:11:34 File Name :ceshi2.cpp ************************************************ */ #inclu

最近公共祖先(lca) hdu 2586

hdu 2586 How far away ? 题目大意:给定n-1条边构成一棵树,无向的:和m个询问,对于每一个询问按顺序回答. 结题思路:lca算法算出最近公共祖先,然后dis[u]+dis[v]-2*dis[father](father是u,v的最近公共祖先),小trick是在构造询问树的时候把权值设成询问对应的输入顺序 #include <iostream> #include <cstdio> #include <cstring> #include <al