洛谷P1967 货车运输 生成树+LCA 倍增

题目链接:https://www.luogu.com.cn/problem/P1967

这题我们可以知道有些边是肯定不会用到的,所以我们想办法去边。把这个图的最大生成树求出来即可。然后求两点的最小限重,用树上倍增求LCA,过程种更新最小限重。
代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
const int maxm=5e4+5;
const int inf=0xfffffff;
struct Node
{
    int x,y,w;
}edge1[maxm];//存原图
struct node
{
    int to,next,value;
}edge2[maxm*2];//存最大生成树
//无向边开两倍
int cnt;
int n,m,q;
int head[maxn];
int f[maxn];//存祖先节点
int fa[maxn][21];//存生成树父节点
int weight[maxn][21];//存最小限重
int depth[maxn];//存每个点深度
void add(int x,int y,int z)//生成树加边
{
    edge2[++cnt].next=head[x];
    edge2[cnt].to=y;
    edge2[cnt].value=z;
    head[x]=cnt;
}
int find(int x)//并查集找祖先
{
    if(x==f[x])
    return x;
    return f[x]=find(f[x]);
}
bool cmp(Node a,Node b)//权值大的优先
{
    return a.w>b.w;
}
void kruskal()
{
    sort(edge1+1,edge1+1+m,cmp);//先排序
    for(int i=1;i<=n;i++)
    f[i]=i;//初始祖先节点是自己
    for(int i=1;i<=m;i++)
    {
        int fx=find(edge1[i].x),fy=find(edge1[i].y);
        if(fx!=fy)
        {
            f[fx]=fy;
            add(edge1[i].x,edge1[i].y,edge1[i].w);
            add(edge1[i].y,edge1[i].x,edge1[i].w);//无向边存两次
        }
    }
}
void dfs(int x)
{
    for(int i=head[x];i;i=edge2[i].next)
    {
        int v=edge2[i].to;
        if(fa[x][0]==v)
        continue;
        int c=edge2[i].value;
        depth[v]=depth[x]+1;
        fa[v][0]=x;//v的父节点为x
        weight[v][0]=c;//存限重
        dfs(v);
    }
}
int gmin(int x,int y)//手写取最小值
{
    if(x>y)
    return y;
    return x;
}
void init()
{
    for(int i=1;i<=20;i++)
    for(int j=1;j<=n;j++)
    {
        fa[j][i]=fa[fa[j][i-1]][i-1];
        weight[j][i]=gmin(weight[j][i-1],weight[fa[j][i-1]][i-1]);
    }
}
int lca(int x,int y)
{
    if(find(x)!=find(y))
    return -1;//不可达
    if(depth[x]>depth[y])
    swap(x,y);//保证y为深度大的
    int ans=inf;//答案
    for(int i=20;i>=0;i--)//使y深度与x深度相等
    if(depth[fa[y][i]]>=depth[x])
    {
        ans=gmin(ans,weight[y][i]);
        y=fa[y][i];
    }
    if(x==y)//已经到了
    return ans;
    for(int i=20;i>=0;i--)
    {
        if(fa[x][i]!=fa[y][i])//父节点不一样才能跳
        {
            ans=gmin(ans,gmin(weight[x][i],weight[y][i]));
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    //别忘了到最近祖先节点还要取一次最小值
    ans=gmin(ans,gmin(weight[x][0],weight[y][0]));
    return ans;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d %d",&edge1[i].x,&edge1[i].y,&edge1[i].w);
    }
    kruskal();
    for(int i=1;i<=n;i++)
    if(!fa[i][0])
    {
        dfs(i);
        fa[i][0]=i;
        weight[i][0]=inf;
    }
    init();//LCA初始化
    scanf("%d",&q);
    while(q--)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/104541501