【NOIP2013提高组】货车运输

https://www.luogu.org/problem/show?pid=1967

思考一下,将图的所有边按边权从大到小依次加入图,则当u与v第一次连通时,刚加入的边就是使u与v两点的路径中的最小边最大的边。

将图的所有边按边权从大到小依次加入图?这不就是Kruscal算法最大生成树吗!

所以我们只需要对原图求最大生成树,对于每个询问求两点的路径上的最小边就可以了。虽然可以用树剖+ST表优化,但是暴力求LCA+暴力爬链也能过了。

暴力LCA+暴力爬链:

#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>
#define maxn 10005
#define maxq 30005
using namespace std;
int n, m;

//
struct edge
{
    int from, to, weight;
};
bool cmp(const edge& x, const edge& y)
{
    return x.weight > y.weight;
}
vector<edge> edges;
vector<int> tree[maxn];
void addedge(int u, int v, int w)
{
    edges.push_back((edge){u, v, w});
    tree[u].push_back(edges.size() - 1);
    edges.push_back((edge){v, u, w});
    tree[v].push_back(edges.size() - 1);
}
bool visited[maxn];
int depth[maxn], parent[maxn];
int weight[maxn];
void buildtree(int v, int fr, int d)
{
    depth[v] = d;
    parent[v] = fr;
    visited[v] = true;
    for (int i = 0; i < tree[v].size(); i++)
    {
        int w = edges[tree[v][i]].to;
        if (w != fr)
        {
            weight[w] = edges[tree[v][i]].weight;
            buildtree(w, v, d + 1);
        }
    }
}
int lca(int x, int y)
{
    while (x != y)
    {
        if (depth[x] < depth[y])
            swap(x, y);
        x = parent[x];
    }
    return x;
}
int minedge(int v, int w)
{
    int x = lca(v, w), ans = 0x7fffffff;
    while (v != x)
    {
        ans = min(ans, weight[v]);
        v = parent[v];
    }
    while (w != x)
    {
        ans = min(ans, weight[w]);
        w = parent[w];
    }
    return ans;
}

// 并查集与Kruscal
namespace djs
{
int parent[maxn];
void init()
{
    for (int i = 1; i <= n; i++)
        parent[i] = -1;
}
int find(int x)
{
    if (parent[x] < 0)
        return x;
    else
        return parent[x] = find(parent[x]);
}
void merge(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x == y)
        return;
    if (parent[x] > parent[y])
        swap(x, y);
    parent[x] += parent[y];
    parent[y] = x;
}
bool related(int x, int y)
{
    return find(x) == find(y);
}
}
vector<edge> e;
void kruscal()
{
    sort(e.begin(), e.end(), cmp);
    djs::init();

    int cnt = 0;
    for (int i = 0; i < e.size(); i++)
    {
        if (!djs::related(e[i].from, e[i].to))
        {
            djs::merge(e[i].from, e[i].to);
            addedge(e[i].from, e[i].to, e[i].weight);
            cnt++;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    int a, b, c;
    for (int i = 1; i <= m; i++)
    {
        cin >> a >> b >> c;
        e.push_back((edge){a, b, c});
    }
    kruscal();
    for (int i = 1; i <= n; i++)
    {
        if (!visited[i])
            buildtree(i, 0, 1);
    }

    int nq;
    cin >> nq;
    for (int i = 1; i <= nq; i++)
    {
        cin >> a >> b;
        if (djs::related(a, b))
            cout << minedge(a, b) << endl;
        else
            cout << -1 << endl;
    }
    return 0;
}

树剖+ST表优化:

#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>
#define maxn 10005
#define maxq 30005
using namespace std;
const int inf = 0x7fffffff;
int n, m;
// ST表
namespace st
{
int logn[maxn];
int dp[maxn][15];
int val[maxn];
void init()
{
    for (int i = 1; i <= n; i++)
    {
        dp[i][0] = val[i];
        logn[i] = log(i) / log(2);
    }

    // f(i,j)=min{f(i,j-1),f(i+2^(j-1),j-1)}
    int limit = logn[n] + 1;
    for (int j = 1; j <= limit; j++)
    {
        for (int i = 1; i <= n; i++)
        {
            if (i + (1 << (j - 1)) <= n)
                dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]);
            else
                dp[i][j] = dp[i][j - 1];
        }
    }
}
int min(int l, int r)
{
    if (l > r)
        return 0x7fffffff;
    // return max{f(l,k),f(r-2^k+1,k)}
    int k = logn[r - l + 1];
    return std::min(dp[l][k], dp[r - (1 << k) + 1][k]);
}
}

// 树与树剖
struct edge
{
    int from, to, weight;
};
bool cmp(const edge &x, const edge &y)
{
    return x.weight > y.weight;
}
vector<edge> edges;
vector<int> tree[maxn];
void addedge(int u, int v, int w)
{
    edges.push_back((edge){u, v, w});
    tree[u].push_back(edges.size() - 1);
    edges.push_back((edge){v, u, w});
    tree[v].push_back(edges.size() - 1);
}
bool visited[maxn];
int size[maxn], depth[maxn], parent[maxn], heavy[maxn];
int weight[maxn];
void buildtree(int v, int fr, int d)
{
    depth[v] = d;
    parent[v] = fr;
    size[v] = 1;
    heavy[v] = 0;
    visited[v] = true;
    for (int i = 0; i < tree[v].size(); i++)
    {
        int w = edges[tree[v][i]].to;
        if (w != fr)
        {
            weight[w] = edges[tree[v][i]].weight;
            buildtree(w, v, d + 1);
            size[v] += size[w];
            if (size[heavy[v]] < size[w])
                heavy[v] = w;
        }
    }
}
int timer = 1;
int top[maxn], hashh[maxn];
void slpf(int v, int tp)
{
    st::val[timer] = weight[v];
    hashh[v] = timer++;
    top[v] = tp;
    if (heavy[v])
    {
        slpf(heavy[v], tp);
        for (int i = 0; i < tree[v].size(); i++)
        {
            int w = edges[tree[v][i]].to;
            if (w != parent[v] && w != heavy[v])
                slpf(w, w);
        }
    }
}
int minedge(int v, int w)
{
    int ans = 0x7fffffff;
    int vp, wp;
    while (true)
    {
        vp = top[v];
        wp = top[w];
        if (vp == wp)
            break;
        if (depth[vp] < depth[wp])
        {
            swap(v, w);
            swap(vp, wp);
        }
        ans = min(ans, st::min(hashh[vp], hashh[v]));
        v = parent[vp];
    }
    if (depth[v] > depth[w])
        swap(v, w);
    ans = min(ans, st::min(hashh[v] + 1, hashh[w]));
    return ans;
}

// 并查集与Kruscal
namespace djs
{
int parent[maxn];
void init()
{
    for (int i = 1; i <= n; i++)
        parent[i] = -1;
}
int find(int x)
{
    if (parent[x] < 0)
        return x;
    else
        return parent[x] = find(parent[x]);
}
void merge(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x == y)
        return;
    if (parent[x] > parent[y])
        swap(x, y);
    parent[x] += parent[y];
    parent[y] = x;
}
bool related(int x, int y)
{
    return find(x) == find(y);
}
}
vector<edge> e;
void kruscal()
{
    sort(e.begin(), e.end(), cmp);
    djs::init();

    int cnt = 0;
    for (int i = 0; i < e.size(); i++)
    {
        if (!djs::related(e[i].from, e[i].to))
        {
            djs::merge(e[i].from, e[i].to);
            addedge(e[i].from, e[i].to, e[i].weight);
            cnt++;
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    int a, b, c;
    for (int i = 1; i <= m; i++)
    {
        cin >> a >> b >> c;
        e.push_back((edge){a, b, c});
    }
    kruscal();
    for (int i = 1; i <= n; i++)
    {
        if (!visited[i])
        {
            buildtree(i, 0, 1);
            slpf(i, i);
        }
    }
    st::init();

    int nq;
    cin >> nq;
    for (int i = 1; i <= nq; i++)
    {
        cin >> a >> b;
        if (djs::related(a, b))
            cout << minedge(a, b) << endl;
        else
            cout << -1 << endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/ssttkkl/p/7531863.html