• 【题解】洛谷P4281 [AHOI2008] 紧急集合(求三个点LCA)


    洛谷P4281:https://www.luogu.org/problemnew/show/P4281

    思路

    答案所在的点必定是个人所在点之间路径上的一点

    本蒟蒻一开始的想法是:先求出2个点之间的LCA 再求出此LCA和第3个点的LCA

    但是没有考虑到有可能答案所在点可能比2个点之间的LCA深度更深

    因为两点之间的LCA是两点共同能到达的深度最浅的一个点

    所以我们可以考虑:

    设a=LCA(x,y) 此时x和y到a点为最小花费 则此时z到a的花费可以用LCA(a,z)来计算

    因此我们分别计算3种情况并取最小值即可

    设d为树的深度 a为x和y的LCA b为a与z的LCA 从x到a y到a 分别花费d[x]-d[a]和d[y]-d[a] 从z到a花费d[z]+d[a]-2*d[b]

    ans=max(d[x]+d[y]+d[z]-d[a]-2*d[b])

    代码

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define maxn 500050
    #define INF 1e9+7
    int n,m,cnt,ans,a,b,k,x,y,z;
    int h[maxn],dep[maxn],f[maxn][30];
    struct Edge
    {
        int next;
        int to;
    }e[maxn<<1];
    void add(int u,int v)
    {
        e[++cnt].to=v;
        e[cnt].next=h[u];
        h[u]=cnt;
    }
    void deal(int u,int fa)//常规预处理 
    {
        dep[u]=dep[fa]+1;
        for(int i=1;i<=21;i++)
        {
            f[u][i]=f[f[u][i-1]][i-1];
        }
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].to;
            if(v==fa) continue;
            f[v][0]=u;
            deal(v,u);
        }
    }
    int lca(int x,int y)//常规LCA 
    {
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=21;i>=0;i--)
        {
            if(dep[f[x][i]]>=dep[y]) x=f[x][i];
            if(x==y) return x;
        }
        for(int i=21;i>=0;i--)
        {
            if(f[x][i]!=f[y][i])
            {
                x=f[x][i];
                y=f[y][i];
            }
        }
        return f[x][0];
    }
    void check()
    {
        int t=dep[x]+dep[y]+dep[z]-dep[a]-2*dep[b];//计算每种情况的ans 
        if(t<ans)//如果当前值小于原ans 则替换 
        {
            ans=t;
            k=a;//k为最后的位置 
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        deal(1,0);//预处理 
        for(int i=1;i<=m;i++)
        {
            ans=INF;//初始化 
            scanf("%d%d%d",&x,&y,&z);
            a=lca(x,y);//三种情况分别计算 
            b=lca(a,z);
            check();
            a=lca(x,z);
            b=lca(a,y);
            check();
            a=lca(z,y);
            b=lca(a,x);
            check();
            printf("%d %d
    ",k,ans);
        }
    }
  • 相关阅读:
    如何使用SQL语句 查看存储过程的内容
    sl第一篇
    winForm连接数据库(sqlserver2005)
    Format
    dual使用
    ThreadLocal与事务
    oracle中的常用函数
    Oracle中merge into的使用
    API设计中token的思路
    SVN常用功能
  • 原文地址:https://www.cnblogs.com/BrokenString/p/9780757.html
Copyright © 2020-2023  润新知