CF1253F Cheap Robot 题解

首先建立一个超级点 \(S\),对于每一个可以充电的点 \(u\) 都建立一条从 \(S\to u\) 的边权为 \(0\)有向边。从这个超级点 \(S\) 开始跑一遍最短路算法,就可以得到每一个点 \(u\) 至少需要花费多少的电量才可以走到一个充电点。令 \(D_i\) 表示 \(i\) 号点最少花费多少可以到一个充电点。

假设当前 Robot 走到了一个点 \(i\),此时 Robot 的剩余电量是 \(remain\),电池的容量是 \(R\) 那么必然有 \(R-D_i\ge remain\ge D_i\)

考虑枚举每一条 \(i\) 点的出边 \(i\to j\),边权为 \(w\),那么必然有 \(R-D_j\ge remain-w\ge D_j\)。合并得到了 \(D_i+D_j+w\le c\)

也就是说,从 \(i\) 点到 \(j\) 点,若至少有一条 \(i\to j\) 的边,且所有 \(i\to j\) 的边边权最小的边的边权是 \(w\),则最少需要的电池容量是 \(D_i+D_j+w\)

但是问题是有 \(Q\) 组询问,不能对于每一组询问都暴力在每一条路径上去搜索出最小的合法答案。

容易发现(很容易吗),若对于每一条边的最小需要电池容量都已经求出,那么考虑建图 \(G\),那么求出 \(G\) 的任意一棵子最小生成树 \(T\),则任何的一对点 \((u,v)\) 的一组花费的电量最小解上的每一条边都一定在这个最小生成树 \(T\) 上。

因为有 \(Q\) 组询问且是静态的,所以使用书上倍增预处理出树 \(T\) 上的每一条路径的 \(\max\) 值,每一次直接 \(\log\) 查询即可。时间复杂度为 \(O((n+Q)\log n)\),可以通过。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 300100;
vector<pair<int, int>> z[N], y[N];
struct { int u, v, w; } ed[N];
int fa[N], dis[N], dep[N], f[N][20], g[N][20], n, m, k, Q;
bool vis[N];
int find(int x) {
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void spfa(int u) {
    queue<int> q;
    q.push(u);
    vis[u] = true;
    memset(dis, 0x3f, sizeof dis);
    dis[u] = 0;
    while (q.size()) {
        int f = q.front();
        q.pop();
        vis[f] = false;
        for (auto &[g, w] : z[f])
            if (dis[g] > dis[f] + w) {
                dis[g] = dis[f] + w;
                if (!vis[g]) vis[g] = true, q.push(g);
            }
    }
}
void dfs(int u, int fa) {
    dep[u] = dep[fa] + 1, f[u][0] = fa;
    for (auto &[v, w] : y[u])
        if (v != fa)
            g[v][0] = w, dfs(v, u);
}
void init() {
    for (int i = 1; i < 20; i++)
        for (int j = 1; j <= n; j++) {
            f[j][i] = f[f[j][i - 1]][i - 1];
            g[j][i] = max(g[j][i - 1], g[g[j][i - 1]][i - 1]);
        }
}
int lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    for (int i = 0; i < 20; i++)
        if ((dep[u] - dep[v]) >> i & 1) u = f[u][i];
    if (u == v) return u;
    for (int i = 19; ~i; i--)
        if (f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
    return f[u][0];
}
int query(int u, int v) {
    int mx = 0;
    if (dep[u] < dep[v]) swap(u, v);
    for (int i = 0; i < 20; i++)
        if ((dep[u] - dep[v]) >> i & 1) mx = max(mx, g[u][i]), u = f[u][i];
    if (u == v) return mx;
    for (int i = 19; ~i; i--)
        if (f[u][i] != f[v][i]) mx = max({mx, g[u][i], g[v][i]}), u = f[u][i], v = f[v][i];
    return max({mx, g[u][0], g[v][0]});
}
signed main() {
    cin >> n >> m >> k >> Q;
    for (int i = 0; i < m; i++) {
        cin >> ed[i].u >> ed[i].v >> ed[i].w;
        z[ed[i].u].push_back({ed[i].v, ed[i].w});
        z[ed[i].v].push_back({ed[i].u, ed[i].w});
    }
    for (int i = 1; i <= k; i++)
        z[0].push_back({i, 0}), z[i].push_back({0, 0});
    spfa(0);
    for (int i = 0; i < m; i++)
        ed[i].w += dis[ed[i].u] + dis[ed[i].v];
    sort(ed, ed + m, [&](auto l, auto r) {
        return l.w < r.w;
    });
    for (int i = 1; i <= n; i++)
        fa[i] = i;
    for (int i = 0; i < m; i++) {
        int ta = find(ed[i].u), tb = find(ed[i].v), c = ed[i].w;
        if (ta != tb) fa[ta] = tb, y[ed[i].u].push_back({ed[i].v, ed[i].w}), y[ed[i].v].push_back({ed[i].u, ed[i].w});
    }
    dfs(1, 0);
    init();
    while (Q--) {
        int u, v;
        cin >> u >> v;
        cout << query(u, v) << '\n';
    }
    return 0;
}
posted @ 2024-04-15 16:15  yhbqwq  阅读(10)  评论(0编辑  收藏  举报