HDU 6031 Innumerable Ancestors (二分+樹上倍增)
Innumerable Ancestors
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1854 Accepted Submission(s): 647
Problem Description
There is a tree having n nodes, labeled from 1 to n. The root of the tree is always 1, and the depth of a node p is the number of nodes on the shortest path between node p and the root.
In computer science, the Lowest Common Ancestor (LCA) of two nodes v and w in a tree is the lowest (i.e. deepest) node that has both v and w as descendants, where we define each node to be a descendant of itself (so if v has a direct connection from w, w is the lowest common ancestor).
You have to answer m queries. Each query gives two non-empty node sets A and B, there might be some nodes in both sets.
You should select one node x from set A, and one node y from set B, x and y can be the same node. Your goal is to maximize the depth of the LCA of x and y.
Please write a program to answer these queries.
Input
The input contains several test cases, no more than 5 test cases.
In each test case, the first line contains two integers n(1≤n≤100000) and m(1≤m≤100000), denoting the number of nodes and queries.
For the next n−1 lines,each line contians two integers a and b, denoting a bi-directional edge between node a and b.
Then there are 2m lines, every two lines describes one query.
For each query, the first line describes the set A.
The first integer k(1≤k≤n) denotes the number of nodes in set A, and the next k integers describing the nodes in set A. There might be some nodes appear multiple times in the set.
The second line describes the set B in the same format of set A.
It is guaranteed that ∑k≤100000 in each test case.
Output
For every query, print a number denoting the answer, which means the maximum depth of the LCA.
Sample Input
7 3
1 2
1 3
3 4
3 5
4 6
4 7
1 6
1 7
2 6 7
1 7
2 5 4
2 3 2
Sample Output
3
4
2
題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6031
題目大意:給兩個集合A,B,求從兩個集合中選出的點的LCA深度最大可以是多少
題目分析:容易發現若x是u和v的lca,則x的父親也必然是u和v的公共祖先,因此可以二分深度,對集合A的每個節點求深度為mid的祖先,插入一個set,對於集合B也求出每個節點深度為mid的祖先,如果在set中,則當前二分的答案可行
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
int const POW = 18;
int const MAX = 100005;
int head[MAX], cnt;
int n, m, dep[MAX], p[MAX][20];
int k1, k2, a[MAX], b[MAX];
set<int> st;
struct EDGE {
int to, nxt;
}e[MAX << 1];
void Init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
void Add(int u, int v) {
e[cnt].to = v;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
void DFS(int u, int fa) {
dep[u] = dep[fa] + 1;
p[u][0] = fa;
for (int i = 1; i <= POW; i++) {
if (p[u][i - 1]) {
p[u][i] = p[p[u][i - 1]][i - 1];
}
}
for (int i = head[u]; i != -1; i = e[i].nxt) {
int v = e[i].to;
if (v != fa) {
DFS(v, u);
}
}
}
int query(int u, int dist) {
if (dist < 0) {
return -1;
}
if (dist == 0) {
return u;
}
for (int i = POW; i >= 0; i--) {
if (dist & (1 << i)) {
u = p[u][i];
}
}
return u;
}
bool judge(int x) {
for (int i = 0; i < k1; i++) {
int v = query(a[i], dep[a[i]] - x);
if (v != -1) {
st.insert(v);
}
}
for (int i = 0; i < k2; i++) {
int v = query(b[i], dep[b[i]] - x);
if (st.count(v)) {
return true;
}
}
return false;
}
int main() {
int u, v;
while (scanf("%d %d", &n, &m) != EOF) {
Init();
for (int i = 1; i < n; i++) {
scanf("%d %d", &u, &v);
Add(u, v);
Add(v, u);
}
DFS(1, 0);
while (m--) {
st.clear();
scanf("%d", &k1);
int maxDep = 1;
for (int i = 0; i < k1; i++) {
scanf("%d", &a[i]);
maxDep = max(maxDep, dep[a[i]]);
}
scanf("%d", &k2);
for (int i = 0; i < k2; i++) {
scanf("%d", &b[i]);
}
int l = 1, r = maxDep, mid = 0, ans = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (judge(mid)) {
ans = mid;
l = mid + 1;
} else {
r = mid - 1;
}
}
printf("%d\n", ans);
}
}
}