题目

给出一棵家族树,树上的节点可以由名字唯一标识。给出若干个查询,查询的内容为两个名字,结果为两个名字的最近公共祖先。 
题目链接: 最近公共祖先

分析

在线的RMQ + LCA 算法,先用dfs将树遍历一遍,每次到达一个节点(无论是从父节点到达还是从它的某个子节点返回),都记录下来该节点id,同时记录下来该节点所在的深度。这样一次dfs之后,将树变成两个数组(一个为按照顺序遍历得到的深度数组A,一个是按照顺序遍历得到的节点id数组B)。 
    要求两个节点的最近公共祖先,可以分别找到两个节点最后被访问时刻在A中的位置p1和p2,然后[p1, p2]区间内的最小深度值所在的位置就是就是这两个节点的最近公共祖先在访问顺序数组A中的位置。 
根据数组B,得到LCA的节点id。 
    求区间最小值,使用RMQ_ST算法。

实现

#include<iostream>
#include<string.h>
#include<queue>
#include<cmath>
#include<unordered_map>
#include<unordered_set>
#include<string>
#include<vector>
using namespace std;
const int inf = 1 << 29;
const int kMax = 200005;
unordered_map<string, int> gName2node;
unordered_map<int, string> gNode2name;
int gVisitNode[2 * kMax]; //按照访问次序,节点的排列
int gVisitDepth[2 * kMax]; //按照访问次序,节点的深度
int gNodeLastVisitSeq[kMax]; //节点最后一次被访问的序号
int dp[2*kMax][21][2];
struct Edge{
int to;
int next;
};
Edge gEdges[kMax];
int gEdgeIndex;
int gHead[kMax]; //从根节点出发,按照dfs方式遍历整个树,将每一步遍历到的节点放到 visit_seq 中
void Dfs(int node, int& cur_seq, int depth){
gVisitDepth[cur_seq] = depth; //记录当前深度,用于后续的区间最小查询
gNodeLastVisitSeq[node] = cur_seq;
gVisitNode[cur_seq++] = node; //进入node节点
for (int e = gHead[node]; e != -1; e = gEdges[e].next){
int v = gEdges[e].to;
Dfs(v, cur_seq, depth+1);
gVisitDepth[cur_seq] = depth;
gNodeLastVisitSeq[node] = cur_seq;
gVisitNode[cur_seq++] = node; //离开node节点
}
} void InsertEdge(int u, int v){
int e = gEdgeIndex++;
gEdges[e].to = v;
gEdges[e].next = gHead[u];
gHead[u] = e;
} void Init(int n){
gEdgeIndex = 0;
memset(gEdges, -1, sizeof(gEdges));
memset(gHead, -1, sizeof(gHead));
memset(dp, 0, sizeof(dp));
} void AddPerson(string person){
if (gName2node.find(person) == gName2node.end()){
int node = gName2node.size();
gName2node[person] = node;
gNode2name[node] = person;
}
}
inline int min(int a, int b){
return a < b ? a : b;
}
int main(){
int n, n1, n2;
string person1, person2;
cin >> n;
Init(n);
for (int i = 0; i < n; i++){
cin >> person1 >> person2;
AddPerson(person1);
AddPerson(person2);
n1 = gName2node[person1];
n2 = gName2node[person2];
InsertEdge(n1, n2);
}
int cur_seq = 0;
Dfs(0, cur_seq, 0);
//树的遍历形成的数组,长度为cur_seq
for (int i = 1; i <= cur_seq; i++){
dp[i][0][0] = gVisitDepth[i - 1];
dp[i][0][1] = gVisitNode[i - 1];
}
for (int j = 0; j <= 20; j++){
dp[0][j][0] = inf;
}
int len = log2(cur_seq);
for (int j = 1; j <= len; j++){
for (int i = 1; i + (1 << j) - 1 <= cur_seq; i++){
if (dp[i][j - 1][0] < dp[i + (1 << (j - 1))][j - 1][0]){
dp[i][j][0] = dp[i][j - 1][0];
dp[i][j][1] = dp[i][j - 1][1];
}
else{
dp[i][j][0] = dp[i + (1 << j - 1)][j - 1][0];
dp[i][j][1] = dp[i + (1 << j - 1)][j - 1][1];
}
}
}
int m;
cin >> m;
for (int i = 0; i < m; i++){
cin >> person1 >> person2;
n1 = gNodeLastVisitSeq[gName2node[person1]] + 1;
n2 = gNodeLastVisitSeq[gName2node[person2]] + 1;
if (n1 > n2)
swap(n1, n2);
int k = log2(n2 - n1 + 1);
int min_depth_node;
if (dp[n1][k][0] < dp[n2 - (1 << k) + 1][k][0]){
min_depth_node = dp[n1][k][1];
}
else{
min_depth_node = dp[n2 - (1 << k) + 1][k][1];
}
cout << gNode2name[min_depth_node] << endl;
}
return 0;
}

hiho_1069_最近公共祖先3的更多相关文章

  1. LCA最近公共祖先 ST+RMQ在线算法

    对于一类题目,是一棵树或者森林,有多次查询,求2点间的距离,可以用LCA来解决.     这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O( ...

  2. 【转】最近公共祖先(LCA)

    基本概念 LCA:树上的最近公共祖先,对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. RMQ:区间最小值查询问题.对于长度为n的 ...

  3. 【并查集】【树】最近公共祖先LCA-Tarjan算法

    最近公共祖先LCA 双链BT 如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表.因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表 ...

  4. 洛谷P3379 【模板】最近公共祖先(LCA)

    P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有 ...

  5. Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】

    一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...

  6. 数据结构作业——sights(最短路/最近公共祖先)

    sights Description 美丽的小风姑娘打算去旅游散心,她走进了一座山,发现这座山有 n 个景点,由于山路难修,所以施工队只修了最少条的路,来保证 n 个景点联通,娇弱的小风姑娘不想走那么 ...

  7. [最近公共祖先] POJ 3728 The merchant

    The merchant Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 4556   Accepted: 1576 Desc ...

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

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 27316   Accept ...

  9. 图论--最近公共祖先问题(LCA)模板

    最近公共祖先问题(LCA)是求一颗树上的某两点距离他们最近的公共祖先节点,由于树的特性,树上两点之间路径是唯一的,所以对于很多处理关于树的路径问题的时候为了得知树两点的间的路径,LCA是几乎最有效的解 ...

随机推荐

  1. Convert.ChangeType转换泛型的性能损失测试

    经常要传入参数包,当时一直是用泛型+ChangeType解决的.测试了下,看来这样确实慢了. 另外,可能都会认为Release发布之后会被优化掉.但测试了Release和Debug结果一样慢,比较失望 ...

  2. Java中的的XML文件读写

    XML简介 要理解XML,HTML等格式,先来理解文档对象模型DOM 根据 DOM,HTML 文档中的每个成分都是一个节点,这些节点组成了一棵树.DOM 是这样规定的:整个文档是一个文档节点每个 HT ...

  3. FTP协议标准命令

    FTP:文件传输协议(File Transfer Protocol) 文件传输协议(FTP)使得主机间可以共享文件.FTP 使用 TCP 生成一个虚拟连接用于控制信息,然后再生成一个单独的 TCP 连 ...

  4. zookeeper系列之七—从远程调用认识zookeeper

    http://www.csdn.net/article/2014-01-02/2817944-zookeeper 在Hadoop的学习过程中,Zookeeper是让很多初学者困惑的技术,远程调用服务是 ...

  5. 关于C指针

    地址概念:内存基本单元是一个字节,一个字节8个位.在定义变量的时候,如int a=10:系统为变量a分配2个字节空间:1000~1001,并在1001~1002中存有数据10.内存中没有变量a,只有1 ...

  6. POJ 3061 Subsequence 尺取法

    转自博客:http://blog.chinaunix.net/uid-24922718-id-4848418.html 尺取法就是两个指针表示区间[l,r]的开始与结束 然后根据题目来将端点移动,是一 ...

  7. Python3基础 多分支结构 if-elif-else

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  8. MYSQL写入数据时报错ERROR 1366 (HY000): Incorrect string value: '\xE8\x8B\xB1\xE5\xAF\xB8...' for c 插入中文不能插入

    先把原先你创建的这个表删除,然后 CREATE TABLE IF NOT EXISTS tdb_goods( goods_id SMALLINT UNSIGNED PRIMARY KEY AUTO_I ...

  9. 数据词典与ABAP类型映射

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  10. STORM_0006_第二个storm_topology:WordCountTopology的代码与运行

    我先试试这个Open Live Writer能不能用. 再在ScribeFire中修改一下已经发布的文章试试看. 这两个写博客的地方都没有原始的编辑器方便,可以插入代码,选择文章的分类.所以以后还有这 ...