[Ahoi2008]Meet 紧急集合(lca+好题/树链剖分/倍增)

题意:传送门

题解:分析从小到大分析,从简单到复杂分析,或许就能启发,如果考虑两个点的话那么这两个点的lca绝对是最优解,其实只要是这两个点路径中的任意一点都可以,到了三个点,那么怎么能使得路径最短,如果跑的路中有多余重复的路(就是比如说a跑过这一段,b还会跑这一段),那么肯定这个就不是最优解,同时可以知道的是这样两两会求出一个lca,也就是下来总共三个lca,可以想到的是一定有两个lca是一样的,并且这个lca是深度最低的,与此同时另外一个lca一定就是最优解了,可以画图验证,也可以用反证法验证,最后要输出总路径,也就是利用差分的思想,假设我们这三个点是a,b,c,求出的三个lca是a1,b1,c1,假设a1为答案,那么:

dis=dep[a]+dep[b]-2*dep[a1]+dep[c]-dep[b1]+dep[a1]-dep[c1];

也就是:dis=dep[a]+dep[b]+dep[c]-dep[a1]-dep[b1]-dep[c1];这是一个轮换式。

这个题就是直接查询,用树链剖分可以,用倍增也可以。

附上代码(树链剖分):

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=5e5+5;
const int maxm=1e6+5;
const int inf=0x7fffffff;
struct edge{int v,next;}e[maxm];
int head[maxn],tot;
void add_edges(int u,int v)
{
    e[++tot].v=v;e[tot].next=head[u];head[u]=tot;
    e[++tot].v=u;e[tot].next=head[v];head[v]=tot;
}
int n,m,a,b,c,ans;
int size[maxn],dep[maxn],fa[maxn];
int sz,pos[maxn],bl[maxn];
void init()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add_edges(x,y);
    }
}
void dfs1(int u)
{
    size[u]=1;
    for(int i=head[u];i;i=e[i].next)
    {
        if(e[i].v==fa[u])continue;
        dep[e[i].v]=dep[u]+1;
        fa[e[i].v]=u;
        dfs1(e[i].v);
        size[u]+=size[e[i].v];
    }
}
void dfs2(int u,int chain)
{
    int k=0;sz++;
    pos[u]=sz;
    bl[u]=chain;
    for(int i=head[u];i;i=e[i].next)
        if(dep[e[i].v]>dep[u]&&size[e[i].v]>size[k])
            k=e[i].v;
    if(k==0)return;
    dfs2(k,chain);
    for(int i=head[u];i;i=e[i].next)
        if(dep[e[i].v]>dep[u]&&k!=e[i].v)
            dfs2(e[i].v,e[i].v);
}
int lca(int x,int y)
{
    while(bl[x]!=bl[y])
    {
        if(dep[bl[x]]<dep[bl[y]])swap(x,y);
        x=fa[bl[x]];
    }
    if(pos[x]>pos[y])swap(x,y);
    return x;
}
void solve()
{
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        int a1=lca(a,b),b1=lca(a,c),c1=lca(b,c);
        if(a1==b1)ans=c1;
        else if(a1==c1)ans=b1;
        else ans=a1;
        printf("%d %d\n",ans,dep[a]+dep[b]+dep[c]-dep[a1]-dep[b1]-dep[c1]);
    }
}
int main()
{
    init();
    dfs1(1);
    dfs2(1,1);
    solve();
    return 0;
}

附上代码(倍增)(加了读入优化):

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    char ch=getchar();
    int f=1,x=0;
    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;
}
const int maxn=5e5+5;
const int maxm=1e6+5;
struct edge{int v,next;}e[maxm];
int head[maxn],tot;
void add_edges(int u,int v)
{
    e[++tot].v=v;e[tot].next=head[u];head[u]=tot;
    e[++tot].v=u;e[tot].next=head[v];head[v]=tot;
}
int n,m;
int fa[maxn][20],deep[maxn];
bool vis[maxn];
void init()
{
    n=read();m=read();
    for(int i=1;i<n;i++)
    {
        int x,y;
        x=read();y=read();
        add_edges(x,y);
    }
}
void dfs(int u)
{
    vis[u]=true;
    for(int i=1;i<=18;i++)
    {
        if(deep[u]<(1<<i))break;
        fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    for(int i=head[u];i;i=e[i].next)
    {
        if(vis[e[i].v])continue;
        deep[e[i].v]=deep[u]+1;
        fa[e[i].v][0]=u;
        dfs(e[i].v);
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y])swap(x,y);
    int d=deep[x]-deep[y];
    for(int i=0;i<=18;i++)
        if((d>>i)&1)x=fa[x][i];
    for(int i=18;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            {x=fa[x][i];y=fa[y][i];}
    if(x==y)return x;
    else return fa[x][0];
}
void solve()
{
    int a,b,c,ans;
    for(int i=1;i<=m;i++)
    {
        a=read();b=read();c=read();
        int a1=lca(a,b),b1=lca(a,c),c1=lca(b,c);
        if(a1==b1)ans=c1;
        else if(a1==c1)ans=b1;
        else ans=a1;
        printf("%d %d\n",ans,deep[a]+deep[b]+deep[c]-deep[a1]-deep[b1]-deep[c1]);
    }
}
int main()
{
    init();
    dfs(1);
    solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/87080773