lca(最近公共祖先)

 

http://codevs.cn/problem/2370

/

 

 

 

 

 

2370 小机房的树

 

时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond         题目描述 Description

小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力

输入描述 Input Description
第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点
输出描述 Output Description

一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。

样例输入 Sample Input

3

1 0 1

2 0 1

3

1 0

2 0

1 2

样例输出 Sample Output

1

1

2

 

数据范围及提示 Data Size & Hint

1<=n<=50000, 1<=m<=75000, 0<=c<=1000

思路:建图有点蒙就是。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <algorithm>
#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include <stdio.h>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
using namespace std;
const int N = 5e4 + 9 ;
const int M = 8e4 + 9 ;
int head[N] , tol , qhead[N] , qtol ;
int dis[N] , pre[N] , vis[N] , ans[M];
int n ;

struct Grafe//建图结构体
{
    int from , to , w , next;
}path[N << 1];

struct node//询问两点最近祖先结构体
{
    int to , id , next ;
}query[M << 1];

void add(int u , int v , int w)//建图
{
    path[tol] = {u , v , w , head[u]};
    head[u] = tol++;
}

void qadd(int u , int v , int id)//加入询问的两点
{
    query[qtol] = {v , id , qhead[u]};
    qhead[u] = qtol++;
}

int get(int x)//查
{
    return x == pre[x] ? x : get(pre[x]);
}

void lca(int u)//dfs 根节点
{
    vis[u] = 1 ;//标记遍历过的点
    for(int i = head[u] ; i != -1 ; i = path[i].next)//遍历所有点
    {
        int v = path[i].to;
        if(vis[v]) continue ;
        dis[v] = dis[u] + path[i].w ;
        lca(v);
        pre[v] = u ;//并
    }
    for(int i = qhead[u] ; i != -1 ; i = query[i].next) //同时询问所有点
    {
        int v = query[i].to;
        if(vis[v])//是关联点之前是否出现过
        {
            int fa = get(v);//最近共同祖先
            ans[query[i].id] = dis[u] + dis[v] - (dis[fa] << 1);
        }
    }
}

void init()
{
    memset(head , -1 , sizeof(head));
    memset(qhead , -1 , sizeof(qhead));
    for(int i = 1 ; i <= n ; i++)
        pre[i] = i ;
}

int main()
{
    scanf("%d" , &n);
    init();
    for(int i = 1 ; i < n ; i++)
    {
        int u , v , w ;
        scanf("%d%d%d" , &u , &v , &w);
        add(u , v , w);//有权双向图
        add(v , u , w);
    }
    int q ;
    scanf("%d" , &q);
    for(int i =1 ; i <= q ; i++)
    {
        int u , v ;
        scanf("%d%d" , &u , &v);
        qadd(u , v , i);//双向
        qadd(v , u , i);
    }
    lca(1);
    for(int i = 1 ; i <= q ; i++)
    {
        printf("%d\n" , ans[i]);
    }

    return 0 ;
}

 

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

Nearest Common Ancestors
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 38731   Accepted: 19247

Description

A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:

lca(最近公共祖先)
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

Source

Taejon 2002   可以直接暴力
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <algorithm>
#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include <stdio.h>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
using namespace std;
int vis[100009];
int vis1[100009];
int main()
{
    int t ;
    scanf("%d" , &t);
    while(t--)
    {
        int n ;
        scanf("%d" , &n);
        memset(vis , 0 , sizeof(vis));
        memset(vis1 , 0 , sizeof(vis1));
        for(int i = 1 ; i < n ; i++)
        {
            int u , v ;
            scanf("%d%d" , &u , &v);
            vis[v] = u ;
        }
        int u , v ;
        scanf("%d%d" , &u , &v);
        while(u)
        {
            vis1[u] = 1 ;
            u = vis[u];
        }
       /* for(int i = 1 ; i <= 5 ; i++)
            cout << vis1[i] << " " ;
        cout << endl ;*/
        while(v && !vis1[v])
        {
          //  cout << v << endl;
            v = vis[v];
        }
        printf("%d\n" , v);
    }
    return 0;
}

 

也可Tarjan

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <algorithm>
#include <iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include <stdio.h>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
using namespace std;
const int N = 1e4 + 9 ;
const int M = 1e4 + 9 ;
int head[N] , tol , qhead[N] , qtol ;
int  pre[N] , vis[N] , ans ;
int n ;

struct Grafe
{
    int from , to  , next;
}path[N << 1];

struct node
{
    int to , next ;
}query[M << 1];

void add(int u , int v)
{
    path[tol] = {u , v , head[u]};
    head[u] = tol++;
}

void qadd(int u , int v)
{
    query[qtol] = {v , qhead[u]};
    qhead[u] = qtol++;
}

int get(int x)
{
    return x == pre[x]? x : get(pre[x]);
}

void lca(int u)
{
    vis[u] = 1 ;
    for(int i = head[u] ; i != -1 ; i = path[i].next)
    {
        int v = path[i].to;
        if(vis[v]) continue ;
        lca(v);
        pre[v] = u ;
    }
    for(int i = qhead[u] ; i != -1 ; i = query[i].next)
    {
        int v = query[i].to;
        if(vis[v])
        {
            int fa = get(v);
            ans = fa ;
        }
    }
}

void init()
{
    memset(vis , 0 , sizeof(vis));
    memset(head , -1 , sizeof(head));
    memset(qhead , -1 , sizeof(qhead));
    tol = 0 ;
    qtol = 0 ;
    ans = 0 ;
    for(int i = 1 ; i <= n ; i++)
        pre[i] = i ;
}

int main()
{
    int t ;
    scanf("%d" , &t);
    while(t--)
    {
        int tt ;
        scanf("%d" , &n);
        init();
        for(int i = 1 ; i < n ; i++)
        {
            int u , v ;
            scanf("%d%d" , &u , &v);
            if(i == 1)
                tt = u ;
            add(u , v);
            add(v , u);
        }
        int u , v ;
        scanf("%d%d" , &u , &v);
        qadd(u , v);
        qadd(v , u);
        lca(tt);
        printf("%d\n" , ans);
    }

    return 0 ;
}

 

 

 

 

上一篇:剑指offer计划19( 搜索与回溯算法中等)---java


下一篇:二叉搜索树的最近公共祖先