原题链接:
http://acm.hdu.edu.cn/showproblem.php?pid=6031
题意:给一个n个节点的树, 并且这棵树始终以1为根节点并定义树的任意节点的深度为这个节点到1的最短距离, 之后给出m个查询, 每个查询包含两个集合A, 和B, 每个集合里各自有相同或不同的k个数, 现在问从这两个集合中各取出一个点, 然后这两个点的LCA的深度最大为多少;
思路:这个题真的让我深深的记住了一些LCA的性质, 首先我们可以转换为, 对于每个集合A中的点, 我们去找一个B集合中的点使得这两个点的深度最大, 最后我们再取最大的一个深度就好了, 但是如何对于每个A都可以快速的找到另一个点呢? 这就要用到一个小小的性质, 我们也可以通过画图的方式很好的理解, 对于某一个点u, 肯定是离这个u越近, 那么这两个点形成的LCA就越大, 这个近我们可以根据dfs的先后顺序来判断。 有了这个性质, 我们就可以对于每一个A都在B中进行二分查找, 找到最近的两个点, 这样求LCA就可以了;
LCA怎么求都行, 我这里是用树链剖分写的。。。因为感觉学了树链剖分之后那个LCA已经忘记怎么写了。。2333。。。。
真的菜。。。。感觉这个东西还是挺明显的就是没有想到。。。。。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxed = 100000 + 10;
struct E
{
int v, bef;
}e[maxed * 2];
int n, m, ans, head[maxed];
int link_cnt, d[maxed], gen[maxed], link_[maxed], son[maxed], size_[maxed], top[maxed], l_[maxed];
int cnt_a, cnt_b, A[maxed], B[maxed];
int main()
{
void add_(int x, int y);
void slove_1(int u, int fa);
void slove_2(int u, int fa, int ufa);
int find_lca(int x, int y);
while (scanf("%d%d", &n, &m) != EOF) {
ans = 1;
memset(head, -1, sizeof(head));
int a, b;
for (int i = 1; i <= n - 1; ++i) {
scanf("%d%d", &a, &b);
add_(a, b);
add_(b, a);
}
d[1] = 1, link_cnt = 0, gen[1] = 1;
slove_1(1, 1);
slove_2(1, 1, 1);
int max_;
while (m--) {
scanf("%d", &cnt_a);
for (int i = 1; i <= cnt_a; ++i) {
scanf("%d", &A[i]);
A[i] = link_[A[i]];
}
scanf("%d", &cnt_b);
for (int i = 1; i <= cnt_b; ++i) {
scanf("%d", &B[i]);
B[i] = link_[B[i]];
}
//cout << cnt_a << "====" << cnt_b << endl;
sort(B + 1, B + 1 + cnt_b);
int len = unique(B + 1, B + 1 + cnt_b) - B - 1;
max_ = 0;
for (int i = 1; i <= cnt_a; ++i) {
int wa = lower_bound(B + 1, B + 1 + len, A[i]) - B;
if (wa == len + 1) //{
//int w = find_lca(l_[B[len]], l_[A[i]]);
max_ = max(max_, d[find_lca(l_[B[len]], l_[A[i]])]);
// cout << l_[B[1]] << "----" << l_[A[i]] << endl;
// cout << "----" << w << endl;
// }
else if (wa == 1) //{
max_ = max(max_, d[find_lca(l_[B[1]], l_[A[i]])]);
//}
else {
max_ = max(max_, d[find_lca(l_[B[wa]], l_[A[i]])]);
max_ = max(max_, d[find_lca(l_[B[wa - 1]], l_[A[i]])]);
}
}
printf("%d\n", max_);
}
}
return 0;
}
void add_(int x, int y)
{
e[ans].v = y;
e[ans].bef = head[x];
head[x] = ans++;
}
void slove_1(int u, int fa)
{
link_[u] = ++link_cnt;
l_[link_cnt] = u;
size_[u] = 1;
son[u] = 0;
for (int i = head[u]; i != - 1; i = e[i].bef) {
int v = e[i].v;
if (v == fa)
continue;
gen[v] = u;
d[v] = d[u] + 1;
slove_1(v, u);
size_[u] += size_[v];
if (!son[u] || size_[v] > size_[son[u]])
son[u] = v;
}
}
void slove_2(int u, int fa, int ufa)
{
top[u] = ufa;
if (son[u])
slove_2(son[u], u, ufa);
for (int i = head[u]; i != -1; i = e[i].bef) {
int v = e[i].v;
if (v == fa || v == ufa || son[u] == v)
continue;
slove_2(v, u, v);
}
}
int find_lca(int x, int y)
{
int l = x, r = y;
int f1 = top[x], f2 = top[y];
while (f1 != f2) {
if (d[f1] < d[f2]) {
swap(f1, f2);
swap(l, r);
}
l = gen[f1];
f1 = top[l];
}
if (d[l] > d[r])
swap(l, r);
return l;
}