1558:聚会 ybt

1558:聚会 ybt 题解(看似很难,其实要是摸清了实质这就是个大水题)

上题目

 

1558:聚会


时间限制: 1000 ms         内存限制: 524288 KB
提交数: 82     通过数: 56

 

【题目描述】

 

原题来自:AHOI 2008

 

Y

岛风景美丽宜人,气候温和,物产丰富。Y 岛上有 N 个城市,有 N1 条城市间的道路连接着它们。每一条道路都连接某两个城市。幸运的是,小可可通过这些道路可以走遍 Y

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

 

小可可,小卡卡和小 YY

 

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

 

由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成。他们会提供给你地图以及若干次聚会前他们所处的位置,希望你为他们的每一次聚会选择一个合适的地点。

 

【输入】

 

第一行两个正整数,N

M

。分别表示城市个数和聚会次数;

 

后面有 N1

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

的;

 

再后面有 M

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

所在的城市编号。

 

【输出】

 

一共有 M

行,每行两个数 PC,用一个空格隔开。表示第 i 次聚会的地点选择在编号为 P 的城市,总共的费用是经过 C

条道路所花费的费用。

 

【输入样例】

 

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

 

【提示】

 

数据范围与提示:

 

40% 的数据中,1N,M2×103

 

 

100% 的数据中,1N,M5×105

乍一看这道题正常人的脑子里都会想到最短路 在不看这一节的标题的情况下

但是这道题的本质却是找规律。。。。在纸上画一画就很容易想到

我们分别找出这三个点的LCA,会发现三个LCA当中必有两个相同的点(可以数学证明但是我不会)

而最终要去的那个点就是除了上述两点之外的第三个点

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+1;
int n,m,tot;
int next[N<<1],head[N<<1],f[N<<1][21],Dep[N<<1],to[N<<1];

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

inline void Add(int from,int pos)
{
    next[++tot]=head[from];
    head[from]=tot;
    to[tot]=pos;
}

inline void Pre(int u,int fa)
{
    Dep[u]=Dep[fa]+1;
    f[u][0]=fa;
    for(int i=1;i<=19;i++)
    f[u][i]=f[f[u][i-1]][i-1];
    for(int i=head[u];i;i=next[i])
    {
        if(to[i]==fa) continue;
        //dis[to[i]]=dis[u]+1;
        Pre(to[i],u);
    }
}

inline int LCA(int x,int y)
{
    if(Dep[x]<Dep[y]) swap(x,y);
    for(int i=19;i>=0;i--)
    {
        if(Dep[f[x][i]]>=Dep[y]) x=f[x][i];
        if(x==y) return x;
    }
    for(int i=19;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}

inline int Q(int x,int y)
{
    return (Dep[x]+Dep[y]-2*Dep[LCA(x,y)]);
}

inline int query(int x,int y,int z,int end)
{
    return (Q(x,end)+Q(y,end)+Q(z,end));
}
int main()
{
    n=read();m=read();
    int x,y,z;
    int A,B,C; 
    for(int i=1;i<n;i++)
    {
        x=read();y=read();
        Add(x,y);
        Add(y,x);
    }
    Pre(1,1);
    for(int i=1;i<=m;i++)
    {
        x=read();y=read();z=read();
        A=LCA(x,y);
        B=LCA(x,z);
        C=LCA(y,z);
        if(A==B) printf("%d %d\n",C,query(x,y,z,C));
        else if(A==C) printf("%d %d\n",B,query(x,y,z,B));
        else printf("%d %d\n",A,query(x,y,z,A));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/smartljy/p/11352845.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值