旅行 {倍增水题请享用}

这里写图片描述


【题解】这题看起来就像倍增0 0 {没有为什么}
三个点要走到一起,必定是其中两个点先走到它们的lca,然后第三个点走过去。所以预处理lca后把三种情况都算出来就可以了。


#include <cstdio>
#include <iostream>
#include <cstring>
#define N 200005
struct edge{ int to,nxt;}e[N<<1];
int n,m,ans,anss,cnt,f[N][20],d[N],q[N],fi[N];
bool bo[N];
    void add(int u,int v)
    {
        e[++cnt].to=v;e[cnt].nxt=fi[u];fi[u]=cnt;
    }
    void lca()
    {
        int h=1,t=1;
        memset(bo,false,sizeof(bo));
        for (bo[q[1]=1]=true;h<=t;++h)
            for (int i=fi[q[h]];i;i=e[i].nxt)
                if (!bo[e[i].to])
                {
                    bo[q[++t]=e[i].to]=true;
                    f[e[i].to][0]=q[h];
                    d[e[i].to]=d[q[h]]+1;
                    for (int j=0,k=q[h];f[k][j];k=f[k][j++])
                        f[e[i].to][j+1]=f[k][j]; 
                }
    }
    int findlca(int u,int v)
    {
        if (d[u]<d[v]) std::swap(u,v);
        for (int j;d[u]>d[v];u=f[u][j-1])
            for (j=1;d[f[u][j]]>d[v];++j);
        for (int j;u!=v;u=f[u][j-1],v=f[v][j-1])
            for (j=1;f[u][j]!=f[v][j];++j);
        return u;
    }
    int calc(int x,int y,int z,int p,int q)
    {
        return d[x]+d[y]-d[z]-d[p]+d[q]-d[p];
    }
int main()
{
    scanf("%d%d\n",&n,&m);
    for (int i=1;i<n;++i)
    {
        int u,v;
        scanf("%d%d\n",&u,&v);
        add(u,v);add(v,u);
    }
    lca();
    for (int i=1;i<=m;++i)
    {
        int x,y,z;
        scanf("%d%d%d\n",&x,&y,&z);
        int f1=findlca(x,y),
            f2=findlca(y,z),
            f3=findlca(x,z);
        int s1=calc(x,y,f1,findlca(f1,z),z),
            s2=calc(y,z,f2,findlca(f2,x),x),
            s3=calc(x,z,f3,findlca(f3,y),y);
        if (s1<=s2 && s1<=s3) ans=f1,anss=s1;
        if (s2<s1 && s2<s3) ans=f2,anss=s2;
        if (s3<s2 && s3<s1) ans=f3,anss=s3;
        printf("%d %d\n",ans,anss);
    }
    return 0; 
}

【题外话】最近做题的时候总是有点恹恹的QAQ。。。如何防止犯困。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值