Innumerable Ancestors HDU - 6031 (LCA + 性质二分)

原题链接:

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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值