[hiho 17]最近公共祖先 三

题目描述

这次是使用在线算法解决这个问题。

两个节点的最近公共祖先就是这两个节点的通路上深度最浅的那个节点。

可以通过一遍深搜把树转成数组:每次经过一个节点(无论是从父节点进入还是从子节点返回)时,把它放入数组。同时要记录每个节点在数组中最后一次出现的位置。

使用RMQ-ST算法预先计算2^k长度区间内深度最浅的节点编号。

对于每次询问,将其转换为两个区间段求解。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>

#define MAX 100005
#define MAX_HASH  200005

using namespace std;

char names[MAX_HASH][50];

vector<int> sons[MAX_HASH];
int last_occurance[MAX_HASH];
int dpth[MAX_HASH];

int data[MAX * 2][25];
int idx, n, q;

unsigned int BKDRHash(char *str)
{
    unsigned int seed = 131;
    unsigned int hash = 0;

    while (*str)
    {
        hash = hash * seed + (*str++);
    }

    return (hash & 0x7FFFFFFF) % MAX_HASH;
}

int get_index(char *str) {
	int res = BKDRHash(str);
	while (strlen(names[res]) != 0 && strcmp(str, names[res])) {
		res++;
		if (res > MAX_HASH) res -= MAX_HASH;
	}
	if (strlen(names[res]) == 0) {
		strcpy(names[res], str);
	}
	return res;
}

void dfs(int root_idx, int depth) {
	dpth[root_idx] = depth;
	/*cout << names[root_idx] << depth << endl;*/
	data[idx][0] = root_idx;
	last_occurance[root_idx] = idx;
	idx++;

	int sons_len = sons[root_idx].size();
	for (int i = 0; i < sons_len; i++) {
		dfs(sons[root_idx][i], depth + 1);
		data[idx][0] = root_idx;
		last_occurance[root_idx] = idx;
		idx++;
	}
}

void pre_calc() {
	//for (int i = 0; i < idx; i++) {
	//	cout << i << ":" << names[data[i][0]] << endl;
	//}
	int t = idx;
	int x = 0;
	while (t) {
		x++;
		t >>= 1;
	}
	x--;
	for (int i = 1; i <= x; i++) {
		for (int j = 0; j <= idx - (1 << i); j++) {
			data[j][i] = dpth[data[j][i - 1]] < dpth[data[j + (1 << (i - 1))][i - 1]] ? data[j][i - 1] : data[j + (1 << (i - 1))][i - 1];
			//cout << j << ‘-‘ << j + (1 << i) - 1 <<‘:‘ << names[data[j][i]] << endl;
		}
	}
}

int main() {
	char s1[50], s2[50];
	int i1, i2;
	int start;

	cin >> n;
	cin >> s1 >> s2;
	i1 = get_index(s1);
	i2 = get_index(s2);
	sons[i1].push_back(i2);
	start = i1;
	while (--n) {
		cin >> s1 >> s2;
		i1 = get_index(s1);
		i2 = get_index(s2);
		sons[i1].push_back(i2);
	}

	dfs(start, 0);
	pre_calc();

	cin >> q;
	while(q--) {
		cin >> s1 >> s2;
		i1 = last_occurance[get_index(s1)];
		i2 = last_occurance[get_index(s2)];
		if (i1 > i2) {
			int t = i1;
			i1 = i2;
			i2 = t;
		}
		int len = i2 - i1 + 1;
		int x = 0;
		while (len) {
			x++;
			len >>= 1;
		}
		x--;
		len = 1 << x;
		cout << names[dpth[data[i1][x]] < dpth[data[i2 - len + 1][x]] ? data[i1][x] : data[i2 - len + 1][x]] << endl;
	}
	return 0;
}
时间: 2024-10-26 22:01:09

[hiho 17]最近公共祖先 三的相关文章

hihoCoder_#1069_最近公共祖先&#183;三(RMQ-ST模板)

#1069 : 最近公共祖先·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上上回说到,小Hi和小Ho使用了Tarjan算法来优化了他们的"最近公共祖先"网站,但是很快这样一个离线算法就出现了问题:如果只有一个人提出了询问,那么小Hi和小Ho很难决定到底是针对这个询问就直接进行计算还是等待一定数量的询问一起计算.毕竟无论是一个询问还是很多个询问,使用离线算法都是只需要做一次深度优先搜索就可以了的. 那么问题就来了,如果每次计算都只针对一个询问进行的话

[hiho 15]最近公共祖先 二

题目描述 这次使用离线算法来解决最近公共祖先的问题. 离线算法可以一遍 dfs 处理完所有的查询,因而需要把查询全部储存起来. 具体的 dfs 过程是: 所有节点最初标记为白色,第一次经过该节点时,将其染成灰色,第二次经过该节点时(即离开该节点时)将其染成黑色. 在 dfs 的某个状态下,白色代表未访问的节点,黑色代表已经访问完该节点为根整个子树,灰色代表正在访问该节点为根的子树. 可以在每个节点处查询与该节点相关的所有查询,每个查询对应有另一个节点X(可能是自身): 如果X是白色,说明还未访问

[hiho 13]最近公共祖先 一

题目描述 由于这个跟后几周是一个系列,这周的做法比较简单. 把第一个人的所有祖先做标记,第二个人向上查找祖先直到找到一个标记过的节点或无法继续为止. 代码其实没什么意思. import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class Main { private static class Person { String name; String father; boolean tag

hihocoder1069最近公共祖先&#183;三(LCA在线算法--DFS+RMQ-ST)

树上任意两点的最近祖先,必定就是这两个节点的最短路径上深度最小的那个点. 例如:下图中,节点7和5,其最短路径为7--4--1--5, 这条路径上深度最小的点为节点1,其深度为1.节点1即为节点7和5的LCA. 因此,要找到任意两个节点的LCA,只需要先找到上述最短路径,再找到最短路径中深度最小的点.而这下面所述LCA在线算法所做的事. LCA在线算法描述(以上图为例): 1.获得“最短路径”(并不是真正的一条路径,包含其他节点,但不影响算法的正确性) 采用DFS遍历整棵树,得到以下数据: (1

hihoCoder#1069 最近公共祖先&#183;三

原题地址 根据提示用Spase Table做 将Tree先展成List,因为数组长度等于边数的2倍,树中边数等于节点数-1,所以List数组只要开2倍节点数大小即可 WA了几次,原来是查询的时候出现左边界大于右边界的情况,所以这种情况要颠倒一下 代码: 1 #include <iostream> 2 #include <vector> 3 #include <string> 4 #include <map> 5 6 using namespace std;

【hihoCoder第十七周】最近公共祖先&#183;三

之前就写的是离线算法.思路就是先序一遍树,记录层数,然后高效RMQ就好.ST和线段树都能过. 以后有时间将之前的在线算法补上. #include <bits/stdc++.h> using namespace std; #define MAXN 100005 #define MAXM 105 #define inf 0x7ffffff int n; struct Edge { int v, next; } edge[MAXN]; int head[MAXN]; int e; void addE

hihoCoder week17 最近公共祖先&#183;三 lca st表

记录dfs序列,dfn[tot] 记录第tot次访问的节点 然后查两点在dfs序中出现的第一次 id[u] id[v] 然后  找 dep[k] = min( dep[i] ) {i 属于 [id[u], id[v]]} 最后dfn[k] 就是所求.. 感觉弄来弄去 就是 在映射... 无非就是 求一段序列深度最小的节点编号 #include <bits/stdc++.h> using namespace std; const int N = 2e5+10; int n, cnt, tot,

最近公共祖先(三种算法)

最近研究了一下最近公共祖先算法,根据效率和实现方式不同可以分为基本算法.在线算法和离线算法.下面将结合hihocoder上的题目分别讲解这三种算法. 1.基本算法 对于最近公共祖先问题,最容易想到的算法就是从根开始遍历到两个查询的节点,然后记录下这两条路径,两条路径中距离根节点最远的节点就是所要求的公共祖先. 题目参见 #1062 : 最近公共祖先·一 附上AC代码,由于记录的方式采取的是儿子对应父亲,所以实现的时候有点小技巧,就是对第一个节点的路径进行标记,查找第二个节点的路径时一旦发现访问到

Solution: 最近公共祖先&#183;一 [hiho一下 第十三周]

题目1 : 最近公共祖先·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho最近发现了一个神奇的网站!虽然还不够像58同城那样神奇,但这个网站仍然让小Ho乐在其中,但这是为什么呢? “为什么呢?”小Hi如是问道,在他的观察中小Ho已经沉迷这个网站一周之久了,甚至连他心爱的树玩具都弃置一边. “嘿嘿,小Hi,你快过来看!”小Ho招呼道. “你看,在这个对话框里输入我的名字,在另一个对话框里,输入你的名字,再点这个查询按钮,就可以查出来……什么!我们居然有同一个