[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\n", ST->LCA(x, y)); 
    }
    delete S; 
    delete G; 
    delete ST; 
    return 0; 
}

谢谢阅读

posted @ 2018-10-10 21:07  Capella  阅读(190)  评论(4编辑  收藏  举报

谢谢光临