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 
#include 
#include 
#include 
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 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);
        }
    }
}

 

你可能感兴趣的:(ACM,二分/三分/两点法,图论,算法题)