Hướng giải của Đường đi tổng số


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 Đường đi tổng số

Thuật toán

Dùng Heavy Light Decomposition để phân tách cây thành các chuỗi, mỗi truy vấn xử lý trong \(O(log^2 N)\).

Sử dụng Heavy-Light Decomposition (HLD) kết hợp Segment Tree để hỗ trợ truy vấn tổng trên đường đi và cập nhật điểm.

  • Bước 1: DFS tính kích thước cây con \(sz[u]\) và xác định heavy child (con có \(sz\) lớn nhất).
  • Bước 2: Decompose (HLD) gán mỗi đỉnh một vị trí \(pos[u]\) trong mảng phẳng, ưu tiên heavy child trước để các heavy path thành đoạn liên tục.
  • Bước 3: Xây dựng Segment Tree trên mảng \(arr[pos[u]] = val[u]\), hỗ trợ cập nhật điểm và truy vấn tổng đoạn.
  • Bước 4: Hàm path_sum(u, v) nhảy chain để truy vấn tổng, mỗi bước nhảy query trên Segment Tree. Số bước \(O(\log N)\), tổng \(O(\log^2 N)\).

Độ phức tạp

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

Hướng dẫn giải

Sử dụng thuật toán phù hợp để giải quyết bài toán.

Mã nguồn C++

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

const int MAXN = 100005;

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

// DFS tinh kich thuoc cay con va heavy child
void dfs(int u, int p) {
    parent[u] = p;
    sz[u] = 1;
    heavy[u] = -1;
    int max_sz = 0;
    for (int v : adj[u]) {
        if (v == p) continue;
        depth[v] = depth[u] + 1;
        dfs(v, u);
        sz[u] += sz[v];
        if (sz[v] > max_sz) {
            max_sz = sz[v];
            heavy[u] = v;
        }
    }
}

// Phan ra cay thanh cac heavy path
void decompose(int u, int h) {
    head[u] = h;
    pos[u] = cur_pos;
    arr[cur_pos] = val[u];
    cur_pos++;
    if (heavy[u] != -1)
        decompose(heavy[u], h);
    for (int v : adj[u]) {
        if (v == parent[u] || v == heavy[u]) continue;
        decompose(v, v);
    }
}

// Segment Tree ho tro tong doan
struct SegTree {
    vector<ll> tree;
    void init(int n) { tree.assign(4 * n, 0); }
    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] = 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] = 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 0;
        if (l <= tl && tr <= r) return tree[nd];
        int tm = (tl + tr) / 2;
        return query(2*nd, tl, tm, l, r) + query(2*nd+1, tm+1, tr, l, r);
    }
} st;

// Truy van tong tren duong di u -> v
ll path_sum(int u, int v) {
    ll res = 0;
    while (head[u] != head[v]) {
        if (depth[head[u]] > depth[head[v]]) swap(u, v);
        res += st.query(1, 0, n-1, pos[head[v]], pos[v]);
        v = parent[head[v]];
    }
    if (depth[u] > depth[v]) swap(u, v);
    res += st.query(1, 0, n-1, pos[u], 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++) cin >> val[i];
    for (int i = 0; i < n-1; i++) {
        int u, v; cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    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 u; ll v; cin >> u >> v;
            st.update(1, 0, n-1, pos[u], v);
        } else {
            int u, v; cin >> u >> v;
            cout << path_sum(u, v) << '\n';
        }
    }
    return 0;
}

Nhận xét

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