POJ1330Nearest Common Ancestors

题意

第一行输入T,有T组数据。
对于每组数据,给出一棵树,先输入n,然后n-1行,每行两个数a,b,表示a是b的父亲;第n行输入两个数A,B表示询问A和B的最近公共祖先。

题解

LCA模板题。建议先学学LCA

有两种方法,分别是Tarjan和倍增,这里说一说倍增。
LCA_倍增是LCA的在线算法,时间和空间复杂度分别是O((n+q)log n)和O(n log n)。
对于这个算法,我们从最暴力的算法开始:
①如果a和b深度不同,先把深度调浅,使他变得和浅的那个一样
②现在已经保证了a和b的深度一样,所以我们只要把两个一起一步一步往上移动,直到他们到达同一个节点,也就是他们的最近公共祖先了。

O(nq)的暴力实现

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int N=10000+5;
vector <int> son[N];
int T,n,depth[N],fa[N],in[N],a,b;
void dfs(int prev,int rt){
    depth[rt]=depth[prev]+1;
    fa[rt]=prev;
    for (int i=0;i<son[rt].size();i++)
        dfs(rt,son[rt][i]);
}
int LCA(int a,int b){
    if (depth[a]>depth[b])
        swap(a,b);
    while (depth[b]>depth[a])
        b=fa[b];
    while (a!=b)
        a=fa[a],b=fa[b];
    return a;
}
int main(){
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            son[i].clear();
        memset(in,0,sizeof in);
        for (int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            son[a].push_back(b);
            in[b]++;
        }
        depth[0]=-1;
        int rt=0;
        for (int i=1;i<=n&&rt==0;i++)
            if (in[i]==0)
                rt=i;
        dfs(0,rt);
        scanf("%d%d",&a,&b);
        printf("%d\n",LCA(a,b));
    }
    return 0;
}

而实际上,一步一步往上移动太慢,我们可以做一个预处理:
fa[i][j]表示节点i往上走2^j次所到达的祖先,那么不难写出转移方程:
fa[i][0]=father[i],fa[i][j]=fa[fa[i][j-1]][j-1]
然后在求LCA的时候,有这样一个性质:(假设a和b深度一样)
设anst[x][y]为节点x网上走y步到达的祖先,对于一个k,如果anst[a][k]==anst[b][k],那么对于k‘(k‘>k),一定有anst[a][k‘]==anst[b][k‘];对于一个k,如果anst[a][k]!=anst[b][k],那么对于k‘(k‘<k),一定有anst[a][k‘]!=anst[b][k‘],而且LCA(a,b)=LCA(anst[a][k],anst[b][k])。
于是求法就渐渐的现行了:
1. 把a和b移到同一深度(设depth[x]为节点x的深度),假设depth[a]<=depth[b],所以我们的目的是把b向上移动i=(depth[b]-depth[a])层,那么,由于之前有预处理的fa数组,我们把i写成二进制形势,然后利用fa数组来在log n的复杂度中完成;
2. 寻找a和b的LCA下一层的两个祖先。利用之前的那个性质,再利用倍增,如果a和b的第2^k个祖先不是同一个,那么把a改为fa[a][k],b改为fa[b][k],k减1;否则直接k减1;当然在这之前要实现确定k的最大值,从大往小处理下去。最终的结果就是fa[a][0]或者fa[b][0]。
注意点:如果a和b在调节深度之后已经是同一个祖先的,那么直接返回a或者b。

LCA倍增算法&POJ1330标程

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int N=10000+5;
vector <int> son[N];
int T,n,depth[N],fa[N][17],in[N],a,b;
void dfs(int prev,int rt){
    depth[rt]=depth[prev]+1;
    fa[rt][0]=prev;
    for (int i=1;(1<<i)<=depth[rt];i++)
        fa[rt][i]=fa[fa[rt][i-1]][i-1];
    for (int i=0;i<son[rt].size();i++)
        dfs(rt,son[rt][i]);
}
int LCA(int a,int b){
    if (depth[a]>depth[b])
        swap(a,b);
    for (int i=depth[b]-depth[a],j=0;i>0;i>>=1,j++)
        if (i&1)
            b=fa[b][j];
    if (a==b)
        return a;
    int k;
    for (k=0;(1<<k)<=depth[a];k++);
    for (;k>=0;k--)
        if ((1<<k)<=depth[a]&&fa[a][k]!=fa[b][k])
            a=fa[a][k],b=fa[b][k];
    return fa[a][0];
}
int main(){
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            son[i].clear();
        memset(in,0,sizeof in);
        for (int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            son[a].push_back(b);
            in[b]++;
        }
        depth[0]=-1;
        int rt=0;
        for (int i=1;i<=n&&rt==0;i++)
            if (in[i]==0)
                rt=i;
        dfs(0,rt);
        scanf("%d%d",&a,&b);
        printf("%d\n",LCA(a,b));
    }
    return 0;
}
时间: 2024-10-06 18:30:33

POJ1330Nearest Common Ancestors的相关文章

poj1330Nearest Common Ancestors以及讲解倍增法求lca

Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20487   Accepted: 10784 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

poj----1330Nearest Common Ancestors(简单LCA)

题目连接  http://poj.org/problem?id=1330 就是构建一棵树,然后问你两个节点之间最近的公共父节点是谁? 代码: 1 /*Source Code 2 Problem: 1330 User: huifeidmeng 3 Memory: 1232K Time: 63MS 4 Language: C++ Result: Accepted 5 6 Source Code 7 */ 8 #include<iostream> 9 #include<vector> 1

Poj1330Nearest Common Ancestors LCA

题意给一颗树,再给一个查询两点之间的最近公共祖先. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> using namespace std; const int maxn = 111111; struct edge { int to; int next; }e[maxn * 10]; int len; int head[max

POJ1330Nearest Common Ancestors——最近公共祖先

http://poj.org/problem?id=1330 给一个有根树,一个查询节点(u,v)的最近公共祖先 836K 16MS #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<string> #include<set> #include<map> cons

poj 1330 Nearest Common Ancestors

题目连接 http://poj.org/problem?id=1330 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,...,

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

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. Node x is an ancestor of

[最近公共祖先] POJ 1330 Nearest Common Ancestors

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

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.