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

codevs3287
之前写过一个pascal版本的,年代有点久远了

既然问最大的运货重量,那么每次都选众多路径中最大的边都显然是对的
那么把原本的图变成一颗最大生成树,选权值最大的n-treenum条边(treenum是联通块数量)
好吧,其实是个森林,
那么现在就变成了求树上简单路径上的最值
树上简单路径,,LCA咯,
同时可以像维护st表一样维护一个树上的RMQ
当然这里不能像st表一样暴力重叠,不能重叠,看看lca的套路,咱们二进制拆分吧
fa[x][i]:x节点向上2的i次方的祖先
di[x][i]:从x节点走到fa[x][i]的最大权值

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N = 1e5 + 5;
int n,m;

struct gragh{
    struct Edge{
        int from,to,w;
        Edge(){}
        Edge(int x,int y,int z):from(x),to(y),w(z){}
        bool operator < (const Edge& a)const{
            return w < a.w;
        }
    }edges[N],be[N];
    int E,f[N],fa[N][20],di[N][20],dep[N];
    bool vis[N];
    vector<int >G[N];
    int F(int x){//并查集
        return f[x]==x?x:(f[x]=F(f[x]));
    }

    inline void link(int x,int y,int z){
        edges[++E]=Edge(x,y,z);
        G[x].push_back(E);
    }

    void build(){
        E=0;
        for (int i=1;i<=n;i++)G[i].clear();
        int x,y,z;
        for (int i=1;i<=n;i++)f[i]=i;
        for (int i=1;i<=m;i++){
            scanf("%d%d%d",&x,&y,&z);
            be[i]=Edge(x,y,z);
            f[F(x)]=F(y);
        }
    }

    void kruskal(){
        int treenum = 0;//forests
        memset(vis,0,sizeof(vis));
        for (int i=1;i<=n;i++)if (!vis[F(i)]){
            treenum++;vis[F(i)]=1;
        }
        for (int i=1;i<=n;i++)f[i]=i;
        sort(be+1,be+m+1);
        int cnt = 0;
        for (int i=m;i>=1;i--){
            int x = be[i].from;
            int y = be[i].to  ;
            if (F(x)==F(y))continue;
            f[F(x)]=F(y);
            cnt++;
            link(x,y,be[i].w);
            link(y,x,be[i].w);
            if (cnt==n-treenum)break;
        }
    }

    void dfs(int x){
        vis[x] = 1;
        for (int i=1;i<=17;i++){
            if(dep[x]<(1<<i))break;
            fa[x][i]=fa[fa[x][i-1]][i-1];
            di[x][i]=min(di[x][i-1],di[fa[x][i-1]][i-1]);
        }
        for (int i=0;i<G[x].size();i++){
            Edge e = edges[G[x][i]];
            if (vis[e.to])continue;
            fa[e.to][0] = x;
            di[e.to][0] = e.w;
            dep[e.to] = dep[x]+1;
            dfs(e.to);
        }
    }

    int lca(int x,int y){
        if (dep[x]<dep[y])swap(x,y);
        int t = dep[x] - dep[y];
        for (int i=0;i<=17;i++)
            if ((1<<i)&t) x = fa[x][i];
        for (int i=17;i>=0;i--)
            if (fa[x][i]!=fa[y][i]){
            x=fa[x][i];y=fa[y][i];
        }
        if (x==y)return x;
        return fa[x][0];
    }

    int ask(int x,int f){//f:father
        int ans = INF;
        int t = dep[x]-dep[f];
        for (int i=0;i<=17;i++)if(t&(1<<i)){
            ans=min(ans,di[x][i]);
            x = fa[x][i];
        }
        return ans;
    }

    void work(){
        build();
        kruskal();
        memset(vis,0,sizeof(vis));
        for (int i=1;i<=n;i++)if(!vis[i])dfs(i);
        int q,x,y;
        scanf("%d",&q);
        while (q--){
            scanf("%d%d",&x,&y);
            if (F(x)!=F(y))puts("-1");
            else {
                int t = lca(x,y);
                x = ask(x,t);
                y = ask(y,t);
                printf("%d
",min(x,y));
            }
        }
    }
}g;

int main(){
    //freopen("truck.in","r",stdin);
    //freopen("truck.out","w",stdout);
    for (;~scanf("%d%d",&n,&m);)g.work();
    return 0;
}

长代码写的要仔细一点

原文地址:https://www.cnblogs.com/cww97/p/7533976.html