AcWing 391. 聚会

题目描述:
Y 岛风景美丽宜人,气候温和,物产丰富。

Y 岛上有 N 个城市(编号 1,2,…,N),有 N−1 条城市间的道路连接着它们。

每一条道路都连接某两个城市。

幸运的是,小可可通过这些道路可以走遍 Y 岛的所有城市。

神奇的是,乘车经过每条道路所需要的费用都是一样的。

小可可,小卡卡和小 YY 经常想聚会,每次聚会,他们都会选择一个城市,使得 3 个人到达这个城市的总费用最小。

由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成。

他们会提供给你地图以及若干次聚会前他们所处的位置,希望你为他们的每一次聚会选择一个合适的地点。

输入格式
第一行两个正整数,N 和 M,分别表示城市个数和聚会次数。

后面有 N−1 行,每行用两个正整数 A 和 B 表示编号为 A 和编号为 B 的城市之间有一条路。

再后面有 M 行,每行用三个正整数表示一次聚会的情况:小可可所在的城市编号,小卡卡所在的城市编号以及小 YY 所在的城市编号。

输出格式
一共有 M 行,每行两个数 Pos 和 Cost,用一个空格隔开,表示第 i 次聚会的地点选择在编号为 Pos 的城市,总共的费用是经过 Cost 条道路所花费的费用。

数据范围
N≤500000,M≤500000
输入样例:

6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6

输出样例:

5 2
2 5
4 1
6 0

思路:

  • a,b,c三点的最近祖先一定是lca(a,b),lca(a,c),lca(b,c)中的一个
  • 要用printf不要用cout,超时了十多次原来是因为这个
    代码:
// https://ac.nowcoder.com/acm/problem/50472

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N = 500010, M = N * 2;
int h[N], e[M], ne[M], idx; 
int depth[N], fa[N][20];

inline int read() {
    int x = 0, f = 1;
    char c = getchar();
    while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}

inline void bfs(int root)
{
    queue<int>q;
    q.push(root);
    depth[root] = 1;
    depth[0] = 0;
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (depth[j] > depth[t] + 1)
            {
                depth[j] = depth[t] + 1;
                fa[j][0] = t;
                q.push(j);
                for (int k = 1; k < 19; k++)
                {
                    fa[j][k] = fa[fa[j][k - 1]][k - 1];
                }
            }
        }
    }
}
inline void add(int a, int b )
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}
inline int lca(int u, int v)
{
    if (depth[u] < depth[v])
    {
        swap(u, v);
    }
    for (int k = 18; k >= 0; k--)
    {
        if (depth[fa[u][k]] >= depth[v])
        {
            u = fa[u][k];
        }
    }
    if (u == v)
    {
        return u;
    }
    for (int k = 18; k >= 0; k--)
    {
        if (fa[u][k] != fa[v][k])
        {
            u = fa[u][k];
            v = fa[v][k];
        }
    }
    return fa[u][0];
}
inline pair<int,int> f(int a, int b,int c)
{
    pair<int, int>res(0,0);
    int t=lca(a, b);
    res.second = t;
    res.first += depth[a] + depth[b] - depth[t] * 2;
    res.first += depth[t] + depth[c] - depth[lca(t,c)] * 2;
    return res;
}
int main()
{
    memset(h, -1, sizeof h);
    memset(depth, 0x3f, sizeof depth);
    int n,m;
    cin >> n >> m;
    int root = 0;
    for (int i = 0; i < n - 1; i++)
    {
        int a, b;
        a = read();
        b = read();
        add(a, b );
        add(b, a );
    }
    root = 1;
    bfs(root);
    while (m--)
    {
        int a, b ,c;
        a = read();
        b = read();
        c = read();
        pair<int, int> t = min(f(a, b, c), min(f(b, c, a), f(a, c, b)));
        printf("%d %d\n", t.second, t.first);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值