[最大生成树][LCA] 货车运输

还是纪念一下
傻逼题

Port key

题意

A 国有 nn 座城市,编号从 11 到 nn,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

正解

显然走的边在最大生成树上
然后感觉是换根DP
但是 min ⁡ \min min抠不掉 l c a lca lca到根
换个思路:直接求 l c a lca lca,在求 L C A LCA LCA的时候取 min ⁡ \min min

min ⁡ \min min的时候顺序写反了😭
感谢young_Zn_Cu大佬给我debug
友链在我的自我介绍里

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
int in{
    int i=0,f=1;char ch=0;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') ch=getchar(),f=-1;
    while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
    return i*f;
}

const int N=4e4+5;
const int INF=1e6;
int n,m,q,fa[N],tot;
int first[N],nxt[N<<1],aim[N<<1],wei[N<<1];
int f[N][25],w[N][25],dep[N];
bool vis[N];

struct Edge{
    int u,v,w;

    Edge(){}
    Edge(int _u,int _v,int _w){
        u=_u,v=_v,w=_w;
    }
    
    friend bool operator < (Edge e,Edge f){
        return e.w<f.w;
    }

}edge[N];

int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}

void ljb(int u,int v,int w){
    ++tot;
    nxt[tot]=first[u];
    first[u]=tot;
    aim[tot]=v;
    wei[tot]=w;
    return;
}

void print_tree(int u,int fa){
    printf("%lld %lld\n",u,fa);
    for(int e=first[u];e;e=nxt[e]){
        int v=aim[e];
        if(v==fa) continue;
        print_tree(v,u);
    }
}

void Kruscal(){
    for(int e=m;e>=1;--e){
        int u=edge[e].u,v=edge[e].v;
        if(get(u)!=get(v)){
            fa[get(u)]=get(v);
            ljb(u,v,edge[e].w);
            ljb(v,u,edge[e].w);
        }
    }
    return;
}

void DFS(int u,int fa){
    vis[u]=true;
    f[u][0]=fa;
    dep[u]=dep[fa]+1;
    for(int e=first[u];e;e=nxt[e]){
        int v=aim[e];
        if(vis[v]) continue;
        if(v==fa) continue;
        w[v][0]=wei[e];
        DFS(v,u);
    }
    return;
}

int lca(int u,int v){
    int r=INF;
    if(dep[u]<dep[v]) swap(u,v);
    for(int l=20;l>=0;--l)
        if(dep[f[u][l]]>=dep[v]){
            // printf("%d %d\n",u,w[u][l]);
            r=min(r,w[u][l]);
            u=f[u][l];
        }
    if(u==v) return r;

    for(int l=20;l>=0;--l)
        if(f[u][l]^f[v][l])
            r=min(r,min(w[u][l],w[v][l])),
            u=f[u][l],v=f[v][l];
    r=min(r,min(w[u][0],w[v][0]));
    return r;
}

signed main(){

    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);

    n=in,m=in;
    for(int i=1;i<=m;++i){
        int u=in,v=in,w=in;
        edge[i]=Edge(u,v,w);
    }

    sort(edge+1,edge+m+1);
    for(int i=1;i<=n;++i)
        fa[i]=i;
    Kruscal();
    for(int i=1;i<=n;++i)
        if(!vis[i]){
            f[i][0]=i;
            w[i][0]=INF;
            DFS(i,0);
        }
    for(int i=1;i<=n;++i) cerr<<i<<' '<<dep[i]<<endl;
    for(int l=1;l<=20;++l)
        for(int u=1;u<=n;++u){
            f[u][l]=f[f[u][l-1]][l-1];
            w[u][l]=min(w[u][l-1],w[f[u][l-1]][l-1]);
        }
    
    // for(int u=1;u<=n;++u){
    //     for(int l=0;l<20;++l)
    //         printf("%d ",w[u][l]);
    //     puts("");
    // }
    
    q=in;
    for(int i=1;i<=q;++i){
        int u=in,v=in;
        if(get(u)^get(v))
            puts("-1");
        else
            printf("%lld\n",lca(u,v));
    }

    // print_tree(1,0);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值