Hướng giải của Cập nhật cạnh


Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.

Tác giả: kienadmin

Lời giải cho Cập nhật cạnh

Thuật toán

Đây là bài toán kinh điển QTREE (SPOJ). Ý tưởng:

  • Gán giá trị cạnh \((p, u)\) (với \(p = parent[u]\)) vào đỉnh con \(u\).
  • Khi truy vấn đường đi \(u \to v\), cần loại trừ LCA vì cạnh của LCA không thuộc đường đi.
  • Dùng HLD + Max Segment Tree như bài path max, nhưng query từ \(pos[LCA] + 1\) đến \(pos[u]\).

Độ phức tạp

\(O((N + Q) \log^2 N)\)

Mã nguồn C++

// Giải thuật hld cho bài toán hld-edge-up\n#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int MAXN = 100005;
const ll INF = 1e18;

int n, q;
vector<pair<int,int>> adj[MAXN];
int parent[MAXN], depth[MAXN], sz[MAXN], heavy[MAXN];
int head[MAXN], pos[MAXN], cur_pos;
ll edge_val[MAXN], arr[MAXN];
pair<int,int> edge_list[MAXN];

void dfs(int u, int p) {
    parent[u] = p;
    sz[u] = 1;
    heavy[u] = -1;
    int max_sz = 0;
    for (auto [v, w] : adj[u]) {
        if (v == p) continue;
        depth[v] = depth[u] + 1;
        edge_val[v] = w;
        dfs(v, u);
        sz[u] += sz[v];
        if (sz[v] > max_sz) {
            max_sz = sz[v];
            heavy[u] = v;
        }
    }
}

void decompose(int u, int h) {
    head[u] = h;
    pos[u] = cur_pos;
    arr[cur_pos] = edge_val[u];
    cur_pos++;
    if (heavy[u] != -1)
        decompose(heavy[u], h);
    for (auto [v, w] : adj[u]) {
        if (v == parent[u] || v == heavy[u]) continue;
        decompose(v, v);
    }
}

struct SegTree {
    vector<ll> tree;
    void init(int n) { tree.assign(4 * n, -INF); }
    void build(int nd, int tl, int tr, ll a[]) {
        if (tl == tr) { tree[nd] = a[tl]; return; }
        int tm = (tl + tr) / 2;
        build(2*nd, tl, tm, a);
        build(2*nd+1, tm+1, tr, a);
        tree[nd] = max(tree[2*nd], tree[2*nd+1]);
    }
    void update(int nd, int tl, int tr, int p, ll v) {
        if (tl == tr) { tree[nd] = v; return; }
        int tm = (tl + tr) / 2;
        if (p <= tm) update(2*nd, tl, tm, p, v);
        else update(2*nd+1, tm+1, tr, p, v);
        tree[nd] = max(tree[2*nd], tree[2*nd+1]);
    }
    ll query(int nd, int tl, int tr, int l, int r) {
        if (l > tr || r < tl) return -INF;
        if (l <= tl && tr <= r) return tree[nd];
        int tm = (tl + tr) / 2;
        return max(query(2*nd, tl, tm, l, r),
                   query(2*nd+1, tm+1, tr, l, r));
    }
} st;

int lca(int u, int v) {
    while (head[u] != head[v]) {
        if (depth[head[u]] > depth[head[v]]) u = parent[head[u]];
        else v = parent[head[v]];
    }
    if (depth[u] > depth[v]) swap(u, v);
    return u;
}

ll edge_path_max(int u, int v) {
    int l = lca(u, v);
    ll res = -INF;
    while (head[u] != head[l]) {
        res = max(res, st.query(1, 0, n-1, pos[head[u]], pos[u]));
        u = parent[head[u]];
    }
    if (u != l)
        res = max(res, st.query(1, 0, n-1, pos[l] + 1, pos[u]));
    while (head[v] != head[l]) {
        res = max(res, st.query(1, 0, n-1, pos[head[v]], pos[v]));
        v = parent[head[v]];
    }
    if (v != l)
        res = max(res, st.query(1, 0, n-1, pos[l] + 1, pos[v]));
    return res;
}

int main() {
    // Doc du lieu dau vao
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> q;
    for (int i = 1; i < n; i++) {
        int u, v, w; cin >> u >> v >> w;
        adj[u].push_back({v, w});
        adj[v].push_back({u, w});
        edge_list[i] = {u, v};
    }
    dfs(1, -1);
    cur_pos = 0;
    decompose(1, 1);
    st.init(n);
    st.build(1, 0, n-1, arr);
    while (q--) {
        int type; cin >> type;
        if (type == 1) {
            int i; ll v; cin >> i >> v;
            auto [u, p] = edge_list[i];
            if (depth[u] < depth[p]) swap(u, p);
            st.update(1, 0, n-1, pos[u], v);
        } else {
            int u, v; cin >> u >> v;
            cout << edge_path_max(u, v) << '\n';
        }
    }
    return 0;
}

Nhận xét

Không có ý kiến tại thời điểm này.