• 【题解】洛谷P1967 [NOIP2013TG] 货车运输(LCA+kruscal重构树)


    洛谷P1967:https://www.luogu.org/problemnew/show/P1967

    思路

    感觉2013年D1T3并不是非常难

    但是蒟蒻还是WA了一次

    从题目描述中看出每个点之间有许多条路径

    而我们需要的是找出整条路径中最大的最小可通过量

    一开始看到题目会想到是不是最大流问题 但是仔细一想其实并不用那么麻烦

    我们只需要用kruscal找出最大生成树即可(因为多条路径中只要挑出最大的即可)

    然后在重构树上考虑怎么取到两点之间的最小值

    我们发现图是一个或者是多个树(没有考虑WA了一次)

    所以我们可以用LCA代替朴素算法查找最小值 用一个m1[x][k]数组维护x到x的2k辈祖先路径中的最小值

    这样题目就可以轻松A过

    代码

    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define maxn 10010
    #define INF 100010
    int n,m,q,k,cnt;
    int fa[maxn],f[maxn][32],m1[maxn][32],h[maxn],dep[maxn];
    //fa为kruscal的父亲数组 f为LCA的父亲数组 
    bool vis[maxn];
    struct Node
    {
        int l;
        int r;
        int w;
    }node[maxn*5];//原图的边
    struct Edge
    {
        int nex;
        int to;
        int w;
    }e[maxn*5];//重构树的边 
    void add(int u,int v,int w)
    {
        e[++cnt].to=v;
        e[cnt].w=w;
        e[cnt].nex=h[u];
        h[u]=cnt;
    }
    bool cmp(Node a,Node b)//从大到小 
    {
        return a.w>b.w;
    }
    int find(int x)
    {
        if(fa[x]!=x)
        fa[x]=find(fa[x]);
        return fa[x];
    }
    void pre()
    {
        for(int i=1;i<=17;i++)
        {    
            for(int j=1;j<=n;j++)
            {
                f[j][i]=f[f[j][i-1]][i-1];
                m1[j][i]=min(m1[j][i-1],m1[f[j][i-1]][i-1]);//从儿子来取 
            }
        }
    }
    int lca(int x,int y)
    {
        if(find(x)!=find(y)) return -1;//如果不在同一棵树中就不能到达 
        int ans=INF;//初始化 
        if(dep[x]<dep[y]) swap(x,y);
        for(int k=17;k>=0;k--)
        {
            if(dep[f[x][k]]>=dep[y])  
            {
                ans=min(ans,m1[x][k]);//取最小值 
                x=f[x][k];
            }
            if(x==y) return ans;
        }
        for(int k=17;k>=0;k--)
        {
            if(f[x][k]!=f[y][k])
            {
                ans=min(ans,min(m1[x][k],m1[y][k]));//取最小值 
                x=f[x][k];
                y=f[y][k];
            }
        }
        ans=min(ans,min(m1[x][0],m1[y][0]));//取父亲的最小值 
        return ans;
    }
    void dfs(int u)
    {
        vis[u]=1;//判断已经在树中 
        for(int i=h[u];i;i=e[i].nex)
        {
            int v=e[i].to;
            if(vis[v]) continue;//如果在树中就跳过 
            dep[v]=dep[u]+1;//记录深度 
            f[v][0]=u;//记录父亲 
            m1[v][0]=e[i].w;//初始化最小值为边权值 
            dfs(v);
        }
        return;
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=m;i++) cin>>node[i].l>>node[i].r>>node[i].w;
        sort(node+1,node+1+m,cmp);
        for(int i=1;i<=m;i++)//kruscal
        {
            if(find(node[i].l)!=find(node[i].r))
            {
                fa[find(node[i].l)]=find(node[i].r);
                add(node[i].l,node[i].r,node[i].w);//重构图 
                add(node[i].r,node[i].l,node[i].w);
                k++;
            }
            if(k==n-1) break;
        }
        for(int i=1;i<=n;i++)//预处理LCA 
            if(!vis[i])//判断是不是同一棵树 
            {
                dep[i]=1;//树根的深度为1 
                dfs(i);
                f[i][0]=i;//树根的父亲为自己 
                m1[i][0]=INF;//树根到父亲的最小值为一个极大值 
            }
        pre();//预处理m1数组和f数组 
        cin>>q;
        for(int i=1;i<=q;i++)
        {
            int x,y;
            cin>>x>>y;
            cout<<lca(x,y)<<endl;
        }
    }
  • 相关阅读:
    bzoj 1040: [ZJOI2008]骑士
    CF Playrix Codescapes Cup Problems Analysis
    C++ 指针[转+原创]
    高斯消元
    NOIP模拟
    线段树合并
    After ZJOI2017 day2
    ZJOI 2017 day2 4.27
    ZJOI 2017 二试 day1 4.26
    ZJOI 2017 二试 day0
  • 原文地址:https://www.cnblogs.com/BrokenString/p/9833246.html
Copyright © 2020-2023  润新知