NKOJ-3773 紧急集合

22 篇文章 0 订阅
17 篇文章 0 订阅

由于没有办法复制过来,而我又很懒 所以只能大家自己去找了

题解

这道题目其实有点偏结论

首先 两个人的话 他们最终集中的点肯定在这两个人之间的路径上

那么这个时候再引入第三个人

将样例画出来 稍微变通一下就会发现
假如你多跑两次LCA的话
最终的结果的两倍就是三条路径长度之和

另外还有几个结论

最终集合的点一定是三个人两两公共祖先中的一个(这个应该不需要证明)
且这个点就是三个公共祖先中不同的那个点

小小地证明一下

每个点在集合时 肯定是要经过和其它的点的公共祖先的
当我们选择相同的那个点作为时
    就会有两个人经过不同的那个点最后到达相同的那个点
    (因为有两条路径是经过了那个点的,不同的点走过的路径要单独看)
反之 就只有一个点经过相同的点病到达不同的点

不同的点和相同的点之间的路径是相同的
所以 相比之下 后者更优

附上对拍代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

int n,m,resa,resb,x,y,z,maxf,fl;
int all=0,star[500123],nxt[1000123],ent[1000123];
int dep[500123],up[500123][20];

inline int input()
{
    char c=getchar();int o;
    while(c>57||c<48)c=getchar();
    for(o=0;c>47&&c<58;c=getchar())o=(o<<1)+(o<<3)+c-48;
    return o;
}

void add(int s,int e)
{
    nxt[++all]=star[s];
    star[s]=all;
    ent[all]=e;
}

void search(int p,int f,int fa)
{
    dep[p]=f;
    up[p][0]=fa;
    for(int i=1;i<=18;i++)up[p][i]=up[up[p][i-1]][i-1];
    for(int bian=star[p],e=ent[bian];bian;bian=nxt[bian],e=ent[bian])
        if(e!=fa)search(e,f+1,p);
}

int LCA(int a,int b)
{
    if(dep[a]<dep[b])swap(a,b);
    fl=dep[a]-dep[b];
    for(int i=0;i<=18;i++)
        if(fl&(1<<i))a=up[a][i];
    if(a==b)return a;
    for(int c=18;c>=0;c--)
        if(up[a][c]!=up[b][c])a=up[a][c],b=up[b][c];
    return up[a][0];
}

int getd(int p)
{
    return dep[p]+dep[resa]-dep[LCA(p,resa)]*2;
}

void mini(int fa,int fb,int fc)
{
    if(fa==fb)resa=fc;
    if(fb==fc)resa=fa;
    if(fa==fc)resa=fb;
    resb=getd(x)+getd(y)+getd(z);
}

int main()
{
//  freopen("In.txt","r",stdin);
    int ca,cb,cc,s,e;
    n=input();m=input();
    for(int i=1;i<n;i++)
    {
        s=input();e=input();
        add(s,e);add(e,s);
    }
    search(1,1,0);
    for(int i=1;i<=m;i++)
    {
        x=input();y=input();z=input();
        ca=LCA(x,y);cb=LCA(x,z);cc=LCA(y,z);
        mini(ca,cb,cc);
        printf("%d %d\n",resa,resb);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值