Hướng giải của Điểm yếu nhất


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

Nhận xét

Tương tự bài toán tổng subtree, nhưng thay vì tính tổng, ta cần tìm giá trị nhỏ nhất trên đoạn. BIT không hỗ trợ truy vấn min trên đoạn, do đó ta dùng Segment Tree.

Ý tưởng giải

Sử dụng Euler Tour Type 1 kết hợp với Segment Tree:

  1. Euler Tour: DFS ghi \(tin[u]\) và \(tout[u]\). Subtree của \(u\) là đoạn \([tin[u], tout[u]]\).
  2. Segment Tree: Xây dựng Segment Tree lưu giá trị nhỏ nhất trên mỗi đoạn. Khởi tạo với \(INF\) (\(10^{18}\)).
  3. Cập nhật: Cập nhật điểm tại vị trí \(tin[u]\) với giá trị mới.
  4. Truy vấn: Tìm min trên đoạn \([tin[u], tout[u]]\).

Độ phức tạp

  • Tiền xử lý: \(O(N)\) cho DFS, \(O(N)\) cho build Segment Tree
  • Mỗi truy vấn: \(O(\log N)\)
  • Tổng: \(O((N + Q) \log N)\)

Code mẫu

// Giải thuật ett cho bài toán ett-subtree-min\n#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN = 200005;
const ll INF = 1e18;
vector<int> adj[MAXN];
int tin[MAXN], tout[MAXN];
ll val[MAXN];
ll tree[4 * MAXN];
int timer_dfs = 0;
int n, q;

void build(int node, int start, int end) {
    if (start == end) {
        tree[node] = INF;
        return;
    }
    int mid = (start + end) / 2;
    build(2 * node, start, mid);
    build(2 * node + 1, mid + 1, end);
    tree[node] = min(tree[2 * node], tree[2 * node + 1]);
}

void seg_update(int node, int start, int end, int pos, ll new_val) {
    if (start == end) {
        tree[node] = new_val;
        return;
    }
    int mid = (start + end) / 2;
    if (pos <= mid)
        seg_update(2 * node, start, mid, pos, new_val);
    else
        seg_update(2 * node + 1, mid + 1, end, pos, new_val);
    tree[node] = min(tree[2 * node], tree[2 * node + 1]);
}

ll seg_query(int node, int start, int end, int l, int r) {
    if (r < start || end < l) return INF;
    if (l <= start && end <= r) return tree[node];
    int mid = (start + end) / 2;
    return min(seg_query(2 * node, start, mid, l, r),
               seg_query(2 * node + 1, mid + 1, end, l, r));
}

void dfs(int u, int parent) {
    tin[u] = ++timer_dfs;
    for (int v : adj[u]) {
        if (v != parent) dfs(v, u);
    }
    tout[u] = timer_dfs;
}

int main() {
    // Doc du lieu dau vao
    ios_base::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, 0);
    build(1, 1, n);

    // Khoi tao Segment Tree voi gia tri ban dau
    for (int i = 1; i <= n; i++) {
        seg_update(1, 1, n, tin[i], val[i]);
    }

    while (q--) {
        int type;
        cin >> type;
        if (type == 1) {
            int u;
            ll new_val;
            cin >> u >> new_val;
            val[u] = new_val;
            seg_update(1, 1, n, tin[u], new_val);
        } else {
            int u;
            cin >> u;
            cout << seg_query(1, 1, n, tin[u], tout[u]) << "\n";
        }
    }
    return 0;
}

Nhận xét

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