Meet 紧急集合 (LCA模板题)

博客围绕vjudge上的一道题展开,题目给定一棵n个点的树,进行m次查询,每次查询3个点,需找出使这3个点到树上某点距离总和最小的点并输出该点和距离。题解采用LCA(最近公共祖先)方法,利用3个点有3个LCA且必有两个相同的特性,选择不同的LCA作为答案来求解。

https://vjudge.net/contest/302013#problem/A

题 意 : 给 定 一 棵 n 个 点 的 树 , m 次 查 询 , 每 次 查 询 3 个 点 , 要 求 这 3 个 点 到 树 上 某 个 点 的 距 离 总 和 最 小 , 输 出 选 定 的 点 和 距 离 题意:给定一棵n个点的树,m次查询,每次查询3个点,要求这3个点到树上某个点 的距离总和最小,输出选定的点和距离 nm33

题 解 : L C A ( 最 近 公 共 祖 先 ) 题解:LCA(最近公共祖先) LCA

可 以 发 现 , 给 定 3 个 点 , 那 么 L C A 也 有 3 个 , 但 是 必 定 有 两 个 L C A 是 相 同 的 , 那 么 我 们 就 要 选 择 另 外 一 个 不 同 的 L C A 作 为 答 案 , 然 后 求 距 离 就 好 了 。 可以发现,给定3个点,那么LCA也有3个,但是必定有两个LCA是相同的,那么我们就要选择另外一个不同的LCA作为答案,然后求距离就好了。 3LCA3LCALCA

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<cmath>
#include<map>
#include<set>
#include<vector> 
using namespace std;
#define inf 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define mem(a,b) memset(a,b,sizeof(a));
#define lowbit(x)  x&-x;  
#define debugint(name,x) printf("%s: %d\n",name,x);
#define debugstring(name,x) printf("%s: %s\n",name,x);
typedef long long ll;
typedef unsigned long long ull;
const double eps = 1e-6;

const int mod = 1e9+7;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN = 10010;
const int DEG = 20;

struct Edge
{
    int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
void addedge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
int fa[MAXN][DEG];
int deg[MAXN];

void BFS(int root)
{
    queue<int>que;
    deg[root] = 0;
    fa[root][0] = root;
    que.push(root);
    while(!que.empty())
    {
        int tmp = que.front();
        que.pop();
        for(int i = 1;i < DEG;i++)
            fa[tmp][i] = fa[fa[tmp][i-1]][i-1];
        for(int i = head[tmp]; i != -1;i = edge[i].next)
        {
            int v = edge[i].to;
            if(v == fa[tmp][0])continue;
            deg[v] = deg[tmp] + 1;
            fa[v][0] = tmp;
            que.push(v);
        }

    }
}
int LCA(int u,int v)
{
    if(deg[u] > deg[v])swap(u,v);
    int hu = deg[u], hv = deg[v];
    int tu = u, tv = v;
    for(int det = hv-hu, i = 0; det ;det>>=1, i++)
        if(det&1)
            tv = fa[tv][i];
    if(tu == tv)return tu;
    for(int i = DEG-1; i >= 0; i--)
    {
        if(fa[tu][i] == fa[tv][i])
            continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}
int dis(int u,int v){
    int lca = LCA(u,v);
    return deg[u]+deg[v]-2*deg[lca];
}
int n,m;
int main(){
    scanf("%d%d",&n,&m);
    int u,v;
    init(); 
    for(int i = 1; i <= n-1; i++){
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    BFS(1);
    int x,y,z,ans;
    while(m--){
        scanf("%d%d%d",&x,&y,&z);
        int l1 = LCA(x,y), l2 = LCA(x,z), l3 = LCA(y,z);
        if(l1 == l2){
            printf("%d ",l3);
            ans = dis(x,l3)+dis(y,l3)+dis(z,l3);
            printf("%d\n",ans);
        }
        else if(l1 == l3){
            printf("%d ",l2);
            ans = dis(x,l2)+dis(y,l2)+dis(z,l2);
            printf("%d\n",ans);
        }
        else if(l2 == l3){
            printf("%d ",l1);
            ans = dis(x,l1)+dis(y,l1)+dis(z,l1);
            printf("%d\n",ans);
        }
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值