Luogu P1967 货车运输题解

Posted railgunforever

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P1967 货车运输题解相关的知识,希望对你有一定的参考价值。

一看到这道题,就想到了某次小生成树

  • 一般对于无向图且不是树的情况,求路径经过边的最小(最大)值且不考虑路径总长,通常考虑最小(最大)生成树

由于求尽量通过边,最大生成树即可

很愉快的,问题转换成了如何求树上两点间的边权最小值。

  • 对于树上路径问题,我们通常考虑倍增或树剖

(但我太菜了,不会树剖)所以就用倍增代替了

也就是kruskal(懒得打prim)+倍增lca 综合时间复杂度 (O)((mlog n))

码风奇特的代码如下:

#include<cstdio>
#include<algorithm>
using namespace std;

inline int Min(int a,int b) {
    return a<b?a:b;
}

inline void swap(int &a,int &b) {
    a^=b^=a^=b;
}

int n,m;

int f[10005];

struct edge {
    int u,v,w;
} e[100005];

struct tree {
    int u[100005],v[100005],w[100005];
    int first[10005],next[100005];
    int tot=0;
    int addedge(int a,int b,int c) {
        ++tot;
        u[tot]=a,v[tot]=b,w[tot]=c;
        next[tot]=first[a];
        first[a]=tot;
    }
} tr;

int cmp(edge a,edge b) {
    return a.w>b.w;
}

int gf(int x) {
    if(x==f[x]) {
        return x;
    }
    f[x]=gf(f[x]);
    return f[x];
}

void merge(int a,int b) {
    int fa=gf(a);
    int fb=gf(b);
    if(fa!=fb) {
        f[fb]=fa;
    }
}

void kruskal() {
    for(int i=1; i<=n; i++) {
        f[i]=i;
    }
    for(int i=1; i<=m; i++) {
        if(gf(e[i].u)!=gf(e[i].v)) {
            merge(e[i].u,e[i].v);
            tr.addedge(e[i].u,e[i].v,e[i].w);
            tr.addedge(e[i].v,e[i].u,e[i].w);
        }
    }
}

int p[10005][25];
int mp[10005][25];
int dep[10005];

int vis[10005];

void dfs(int x) {
    vis[x]=1;
    for(int i=tr.first[x]; i; i=tr.next[i]) {
        if(!vis[tr.v[i]]) {
            p[tr.v[i]][0]=x;
            mp[tr.v[i]][0]=tr.w[i];
            dep[tr.v[i]]=dep[x]+1;
            dfs(tr.v[i]);
        }
    }
}

void init() {
    for(int i=1; i<=n; i++) {
        dfs(i);
    }
    for(int j=1; j<=20; j++) {
        for(int i=1; i<=n; i++) {
            p[i][j]=p[p[i][j-1]][j-1];
            mp[i][j]=Min(mp[i][j-1],mp[p[i][j-1]][j-1]);
        }
    }
}

int lca(int a,int b) {
    if(dep[a]<dep[b]) {
        swap(a,b);
    }
    for(int i=20; i>=0; i--) {
        if(dep[a]-(1<<i)>=dep[b]) {
            a=p[a][i];
        }
    }
    if(a==b) {
        return a;
    }
    for(int i=20; i>=0; i--) {
        if(p[a][i]!=p[b][i]) {
            a=p[a][i];
            b=p[b][i];
        }
    }
    return p[a][0];
}

int work(int a,int b) {
    int l=lca(a,b);
    int ans=1<<30;
    if(a!=l) {
        for(int i=20; i>=0; i--) {
            if(dep[p[a][i]]>dep[l]) {
                ans=Min(ans,mp[a][i]);
                a=p[a][i];
            }
        }
        ans=Min(ans,mp[a][0]);
    }
    if(b!=l) {
        for(int i=20; i>=0; i--) {
            if(dep[p[b][i]]>dep[l]) {
                ans=Min(ans,mp[b][i]);
                b=p[b][i];
            }
        }
        ans=Min(ans,mp[b][0]);
    }
    return ans;
}

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++) {
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    }
    sort(e+1,e+m+1,cmp);
    kruskal();
    init();
    int q;
    scanf("%d",&q);
    for(int i=1; i<=q; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        if(gf(a)!=gf(b)) {
            printf("-1
");
            continue;
        }
        printf("%d
",work(a,b));
    }
    return 0;
}

以上是关于Luogu P1967 货车运输题解的主要内容,如果未能解决你的问题,请参考以下文章

洛谷 P1967 货车运输 题解

[luogu P1967][NOIp2013]P1967 货车运输

Luogu P1967 货车运输

luogu P1967 货车运输 最大生成树 倍增LCA

LUOGU P1967 货车运输(最大生成树+树剖+线段树)

P1967 货车运输 题解