• [Luogu 1967] NOIP2013 货车运输


    [Luogu 1967] NOIP2013 货车运输

    <题目链接>


    一年多前令我十分头大的老题终于可以随手切掉了…

    然而我这码风又变毒瘤了,我也很绝望。

    看着一年前不带类不加空格不空行的清纯码风啊,时光也好像回去了一样。//其实一年前我在这题上试过写类来着,结果,当然是写挂了啊。

    众人:别废话了,赶紧讲题!

    Capella:好,好…


    对于每两个点之间的路径,我们希望路上的最小限重尽可能大,以确保运输更多的货物。所以,我们总是会选择限重尽可能大的路去走。

    于是对原图(的每一个连通块)求最大生成树,将问题转化为树上问题。两个点间的的路径可通过求 LCA(最近公共祖先)得到。

    LCA 考虑倍增算法(树剖不想写了),使用两个 Sparse Table(通称 ST 表),一个存树上路径,一个存树上限重最小值。

    讲具体些,f[i][j] 记录编号为 i 的点向上走 (2^j) 步到达的点,g[i][j]记录从 i 到 f[i][j] 这段路径中的最小限重。

    递推预处理,然后在线询问就好。

    如果你刚才用了 Kruskal 求 MST,那么 Kruskal 算法过程中用过的并查集不要扔,洗干净裹上面粉,蛋液,面包糠,下锅炸至两面金黄后捞出,隔壁家的熊孩子都馋哭了。对于每一组询问 (x, y),看一眼两个点是否属于同一并查集,不是就直接 -1。

    如果你用了 Prim,记得跑的时候记一下连通块,用于判断询问的两个点是否连通。

    跑就行了,求路径上最小限重。

    上代码

    #include <algorithm>
    #include <climits>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    
    const int MAXN=100010, MAXM=500010; 
    
    int n, m, q; 
    
    struct Edge
    {
        int u, v, w; 
        void Read(void)
        {
            scanf("%d %d %d", &u, &v, &w); 
        }
        bool operator <(const Edge &rhs) const
        {
            return w>rhs.w; 
        }
    }s[MAXM]; 
    
    struct Graph
    {
        int *depth; 
        struct Edge
        {
            int to, w; 
            Edge *next; 
            Edge(int to, int w, Edge* next): to(to), w(w), next(next){}
            ~Edge(void)
            {
                if(next!=nullptr)
                    delete next; 
            }
        }**head; 
        Graph(int n): depth(new int[n+1]), head(new Edge*[n+1])
        {
            memset(depth, 0, (n<<2)+4); 
            for(int i=1; i<=n; ++i)
                head[i]=nullptr; 
        }
        ~Graph(void)
        {
            delete[] depth; 
            for(int i=1; i<=n; ++i)
                delete head[i]; 
            delete[] head; 
        }
        void AddEdges(int u, int v, int w)
        {
            head[u]=new Edge(v, w, head[u]); 
            head[v]=new Edge(u, w, head[v]); 
        }
    }*G; 
    
    class UFS
    {
        private: 
            int *f; 
        public: 
            UFS(int n): f(new int[n+1])
            {
                for(int i=1; i<=n; ++i)
                    f[i]=i; 
            }
            ~UFS(void)
            {
                delete[] f; 
            }
            int Find(int x)
            {
                return x==f[x] ? x : f[x]=Find(f[x]); 
            }
            bool Merge(int x, int y)
            {
                int a=Find(x), b=Find(y); 
                if(a==b)
                    return false; 
                f[b]=a; 
                return true; 
            }
    }*S; 
    
    class SparseTable
    {
        private: 
            int N, **f, **g; 
            void DFS(int u, int k)
            {
                G->depth[u]=k; 
                int v; 
                for(auto i=G->head[u]; i!=nullptr; i=i->next)
                    if(!G->depth[v=i->to])
                    {
                        f[v][0]=u; 
                        g[v][0]=i->w; 
                        DFS(v, k+1); 
                    }
            }
        public: 
            SparseTable(int n): N(log2(n)), f(new int*[n+1]), g(new int*[n+1])
            {
                for(int i=1; i<=n; ++i)
                {
                    f[i]=new int[N]; 
                    g[i]=new int[N]; 
                }
                for(int i=1; i<=n; ++i)
                    if(!G->depth[i])
                    {
                        f[i][0]=i; 
                        g[i][0]=INT_MAX; 
                        DFS(i, 1); 
                    }
                for(int j=1; j<=N; ++j)
                    for(int i=1; i<=n; ++i)
                    {
                        f[i][j]=f[f[i][j-1]][j-1]; 
                        g[i][j]=std::min(g[i][j-1], g[f[i][j-1]][j-1]); 
                    }
            }
            ~SparseTable(void)
            {
                for(int i=1; i<=n; ++i)
                {
                    delete[] f[i]; 
                    delete[] g[i]; 
                }
                delete[] f; 
                delete[] g; 
            }
            int LCA(int x, int y)
            {
                if(S->Find(x)^S->Find(y))
                    return -1; 
                int ans=INT_MAX; 
                if(G->depth[x]<G->depth[y])
                    std::swap(x, y); 
                for(int i=N; i>=0; --i)
                    if(G->depth[f[x][i]]>=G->depth[y])
                    {
                        ans=std::min(ans, g[x][i]); 
                        x=f[x][i]; 
                    }
                if(x==y)
                    return ans; 
                for(int i=N; i>=0; --i)
                    if(f[x][i]^f[y][i])
                    {
                        ans=std::min(ans, std::min(g[x][i], g[y][i])); 
                        x=f[x][i]; 
                        y=f[y][i]; 
                    }
                return ans=std::min(ans, std::min(g[x][0], g[y][0])); 
            }
    }*ST; 
    
    void Kruskal(void)
    {
        std::sort(s+1, s+m+1); 
        S=new UFS(n); 
        G=new Graph(n); 
        for(int i=1; i<=m; ++i)
            if(S->Merge(s[i].u, s[i].v))
                G->AddEdges(s[i].u, s[i].v, s[i].w); 
    }
    
    int main(void)
    {
        scanf("%d %d", &n, &m); 
        for(int i=1; i<=m; ++i)
            s[i].Read(); 
        Kruskal(); 
        ST=new SparseTable(n); 
        scanf("%d", &q); 
        for(int i=1, x, y; i<=q; ++i)
        {
            scanf("%d %d", &x, &y); 
            printf("%d
    ", ST->LCA(x, y)); 
        }
        delete S; 
        delete G; 
        delete ST; 
        return 0; 
    }
    

    谢谢阅读

  • 相关阅读:
    IOS数据库操作SQLite3使用详解(转)
    AutoLayout 之NSLayoutConstraint
    自动布局autolayout和sizeclass的使用
    IOS 基于TCP的socket通信详解(原创)
    Spring boot AOP 记录请求日志
    Spring boot 异步线程池
    配合 jekins—springboot脚本
    CentOS6 Squid代理服务器的安装与配置
    Redis4.0.1的安装及哨兵模式的配置
    mysql修改最大连接数
  • 原文地址:https://www.cnblogs.com/Capella/p/9769068.html
Copyright © 2020-2023  润新知