Hướng giải của Li Chao bền vững


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

Huong dan giai

Bai toan yeu cau luu giu lich su cac phien ban cua Li Chao Tree. Ta su dung Persistent Li Chao Tree.

Y tuong
  • Moi nut cua cay luu mot duong thang va hai con tro (trai, phai) la chi so trong mang.
  • Khi them duong thang, thay vi sua nut cu, ta tao nut moi tren duong di tu goc den la.
  • Cac nhanh khong bi anh huong se duoc tai su dung (giong Persistent Segment Tree).
  • Moi phien ban co mot root index rieng.
  • Truy van: di tu root cua phien ban do, theo cac con tro.
Do phuc tap
  • Them: \(O(\log M)\) thoi gian va bo nho.
  • Truy van: \(O(\log M)\).
  • Tong bo nho: \(O(Q \log M)\).

Ma nguon C++

// Giải thuật lc cho bài toán lc-persist\n#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 4e18;

struct Node {
    ll m, c;
    int lc, rc;
    Node() : m(0), c(INF), lc(0), rc(0) {}
    Node(ll m, ll c) : m(m), c(c), lc(0), rc(0) {}
    ll eval(ll x) const { return m * x + c; }
};

struct PersistentLiChao {
    vector<ll> xs;
    vector<Node> nodes;
    vector<int> roots;
    int n;
    int cur_root;

    PersistentLiChao(vector<ll> qx) : xs(qx) {
    sort(xs.begin(), xs.end());
    // Sap xep mang
        xs.erase(unique(xs.begin(), xs.end()), xs.end());
        n = xs.size();
        nodes.emplace_back();
        nodes.emplace_back(0, INF);
        roots.push_back(1);
        cur_root = 1;
    }

    int add_line(int old_root, ll m, ll c, int l, int r) {
        int node = nodes.size();
        nodes.push_back(nodes[old_root]);
        int mid = (l + r) / 2;
        Node &cur = nodes[node];
        bool mb = (m * xs[mid] + c) < cur.eval(xs[mid]);
        if (mb) { swap(cur.m, m); swap(cur.c, c); }
        if (l == r) return node;
        bool lb = (m * xs[l] + c) < cur.eval(xs[l]);
        if (lb != mb) cur.lc = add_line(cur.lc, m, c, l, mid);
        else cur.rc = add_line(cur.rc, m, c, mid + 1, r);
        return node;
    }

    void add_line(ll m, ll c) {
        cur_root = add_line(cur_root, m, c, 0, n - 1);
        roots.push_back(cur_root);
    }

    ll query(int idx, int node, int l, int r) {
        if (node == 0) return INF;
        ll res = nodes[node].eval(xs[idx]);
        if (l == r) return res;
        int m = (l + r) / 2;
        if (idx <= m) return min(res, query(idx, nodes[node].lc, l, m));
        else return min(res, query(idx, nodes[node].rc, m + 1, r));
    }

    ll query(int version, ll x) {
        int root = (version < (int)roots.size()) ? roots[version] : 0;
        int idx = lower_bound(xs.begin(), xs.end(), x) - xs.begin();
        return query(idx, root, 0, n - 1);
    }
};

int main() {
    // Doc du lieu dau vao
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int Q; cin >> Q;
    struct Op { int t; ll a, b; int k; ll x; };
    vector<Op> ops(Q);
    vector<ll> qx;
    for (int i = 0; i < Q; i++) {
        int t; cin >> t;
        if (t == 1) { ll a, b; cin >> a >> b; ops[i] = {t, a, b, 0, 0}; }
        else { int k; ll x; cin >> k >> x; ops[i] = {t, 0, 0, k, x}; qx.push_back(x); }
    }
    if (qx.empty()) return 0;
    PersistentLiChao plichao(qx);
    int version = 0;
    for (auto &op : ops) {
        if (op.t == 1) { plichao.add_line(op.a, op.b); version++; }
        else cout << plichao.query(op.k, op.x) << "\n";
    }
    return 0;
}

Nhận xét

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