[題解](最小生成樹/LCA)luogu_P1967貨車運輸

一道好題不出所料又抄的題解


 

1.首先對於這張圖肯定要考慮走哪些邊不走哪些邊,發現我們想要的肯定那些邊權最大的邊,所以想到最大生成樹

這樣能保證選到盡量大的邊

2.跑完最大生成樹后每兩點之間就有唯一路徑了,想要知道兩點間最小邊權,可以在LCA過程中求出(我竟然不會LCA),對lca做些許改動

#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
const int maxm=50010;
struct node1{
    int u,v,w;
}e1[maxm];
struct node2{
    int v,w,nxt;
}e2[maxm*2];
int head[maxn],cnt;
int n,m;
int dep[maxn],f[maxn],fa[maxn][21],w[maxn][21];
//f数组表示并查集中的父节点,fa数组表示树上的父节点,w数组表示最大载重 
bool v[maxn];
void add(int u,int v,int w){
    e2[++cnt].v=v;e2[cnt].w=w;e2[cnt].nxt=head[u];head[u]=cnt;
}
bool cmp(node1 x,node1 y){
    return x.w>y.w;
}
int find(int x){
    while(x!=f[x])x=f[x]=f[f[x]];return x;
}
void kruskal(){
    sort(e1+1,e1+1+m,cmp);
    for(int i=1;i<=n;i++)f[i]=i;
    for(int i=1;i<=m;i++){
        int x=find(e1[i].u),y=find(e1[i].v);
        if(x==y)continue;
        f[x]=y;
        add(e1[i].u,e1[i].v,e1[i].w);
        add(e1[i].v,e1[i].u,e1[i].w);
    }
}
void dfs(int x){
    v[x]=1;
    for(int i=head[x];i;i=e2[i].nxt){
        int y=e2[i].v;
        if(v[y])continue;
        dep[y]=dep[x]+1;
        fa[y][0]=x;//儲存父節點 
        w[y][0]=e2[i].w;
        dfs(y);
    }
}
int lca(int x,int y){//lca過程中求邊權最小值 
    if(find(x)!=find(y))return -1;//不連通 
    int ans=0x7fffffff;
    if(dep[x]>dep[y])swap(x,y);
    //將y節點提升到x相同高度 
    for(int i=20;i>=0;i--)
    if(dep[fa[y][i]]>=dep[x])
    ans=min(ans,w[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=min(ans,min(w[x][i],w[y][i]));
        x=fa[x][i];y=fa[y][i];
    }
    ans=min(ans,min(w[x][0],w[y][0]));
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)scanf("%d%d%d",&e1[i].u,&e1[i].v,&e1[i].w);
    kruskal();
    for(int i=1;i<=n;i++)
    if(!v[i]){
        dep[i]=1;
        dfs(i);
        fa[i][0]=i;
        w[i][0]=0x7fffffff;
    }
    //lca初始化 
    for(int i=1;i<=20;i++)
    for(int j=1;j<=n;j++){
        fa[j][i]=fa[fa[j][i-1]][i-1];
        w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
    }
    int q;
    scanf("%d",&q);
    while(q--){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
}

 

posted @ 2019-04-19 20:27  羊肉汤泡煎饼  阅读(137)  评论(0编辑  收藏  举报