luogu P1396 营救

原题链接:https://www.luogu.org/problem/show?pid=1396

 

刚一看到这个题,马上就想起来前几天刚做的货车运输,于是迅速地敲了个最小生成树+lca求路径上最长边,写完后一看标签和难度,咦,为什么没有lca的标签啊,为什么这个题的难度只有普及/提高减啊,这个不是和货车运输差不多的吗。。。

于是愉快的交上去A掉了之后,仔细想了想新的做法,又想出两种做法:

1.倍增lca+最小生成树:

基本上同货车运输:

 

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
void read(int &y)
{
    y=0;char x=getchar();
    while(x<'0'||x>'9') x=getchar();
    while(x>='0'&&x<='9')
    {
        y=y*10+x-'0';
        x=getchar();
    }
}
struct edge
{
    int u,v,w;
}e[50005];
bool cmp(edge x,edge y)
{
    return x.w<y.w;
}
int fa[10005][25],maxw[10005][25];
int f[10005],head[20005],nxt[20005],to[20005],d[20005],dep[10005];
int n,m,s,t,cnt;
int find(int x)
{
    if(x==f[x]) return x;
    return f[x]=find(f[x]);
}
void add(int u,int v,int w)
{
    to[cnt]=v;
    d[cnt]=w;
    nxt[cnt]=head[u];
    head[u]=cnt++;
}
void dfs(int x,int y)
{
    for(int i=head[x];i!=-1;i=nxt[i])
    {
        if(i!=y)
        {
            dep[to[i]]=dep[x]+1;
            fa[to[i]][0]=x;
            maxw[to[i]][0]=d[i];
            dfs(to[i],i^1);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]>dep[y])
    {
        int tmp=x;
        x=y;
        y=tmp;
    }
    int ans=-1;
    for(int i=15;i>=0;i--)
    {
        if(dep[fa[y][i]]>=dep[x])
        {
            ans=max(ans,maxw[y][i]);
            y=fa[y][i];
        }
    }
    if(x==y) return ans;
    for(int i=15;i>=0;i--)
    {
        if(fa[x][i]!=fa[y][i])
        {  
            ans=max(ans,max(maxw[x][i],maxw[y][i]));  
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    return ans;
}
int main()
{
    read(n);read(m);read(s);read(t);
    for(int i=0;i<m;i++)
    {
        read(e[i].u);read(e[i].v);read(e[i].w);
    }
    sort(e,e+m,cmp);
    memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=0;i<m;i++)
    {
        if(find(e[i].u)!=find(e[i].v))
        {
            add(e[i].u,e[i].v,e[i].w);
            add(e[i].v,e[i].u,e[i].w);
            f[find(e[i].u)]=find(e[i].v);
        }
    }
    dep[1]=1,dfs(1,-1);
    for(int j=1;j<=15;j++)
    {
        for(int i=1;i<=n;i++)
        {
            fa[i][j]=fa[fa[i][j-1]][j-1];
            maxw[i][j]=max(maxw[i][j-1],maxw[fa[i][j-1]][j-1]);
        }
    }
    printf("%d",lca(s,t));
    return 0;
}

 

2.最小生成树+暴力公共祖先:

此题只有一组数据,所以我们可以使用离线算法,首先求出图的最小生成树,然后由s节点暴力向上找父亲节点,一路标记,到达根节点后停止。在由t节点向上暴力找父亲节点,到达有标记的点之后停止。

然后两点分别再向上到公共祖先,更新最大值,输出即可。

3.最小生成树+巧妙利用并查集性质:

继续使用离线算法,然后使用克鲁斯卡尔算法求最小生成树,当s与t在一个集合中时,s到t的路径就确定了,最长的边就是当前要处理的边,输出即可。

 

posted @ 2017-09-23 21:27  Excim  阅读(141)  评论(0编辑  收藏  举报