Hướng giải của Them va xoa duong thang
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.
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ả:
Huong dan giai
Bai toan yeu cau ca them va xoa duong thang. Li Chao Tree thuan khong ho tro xoa, nen ta can xu ly offline bang ky thuat Cay phan doan tren thoi gian (Segment Tree over Time) ket hop DFS + Rollback.
Y tuong
- Doc toan bo thao tac offline.
- Moi duong thang duoc them tai thoi diem \(t_start\) va bi xoa tai \(t_end\) (hoac ton tai den het).
- Xay dung cay phan doan tren mien thoi gian \([1, Q]\).
- Voi moi duong thang, them no vao \(O(\log Q)\) nut cua cay phan doan.
- DFS tren cay phan doan, tai moi nut, them toan bo duong thang cua nut do vao Li Chao Tree.
- Khi tham mot la la thoi diem co truy van, thuc hien truy van tren Li Chao.
- Khi backtrack, phuc hoi lai Li Chao bang rollback (luu vet cac thay doi).
Do phuc tap
\(O(Q \log^2 Q)\).
Ma nguon C++
// Giải thuật lc cho bài toán lc-line-add-rm\n#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 4e18;
struct Line {
ll m, c;
ll eval(ll x) const { return m * x + c; }
};
struct LiChao {
vector<Line> tree;
vector<pair<int, Line>> hist;
int n;
vector<ll> xs;
LiChao(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();
tree.assign(4 * n, {0, INF});
}
void add(Line nw, int node, int l, int r) {
int m = (l + r) / 2;
Line &cur = tree[node];
bool mb = nw.eval(xs[m]) < cur.eval(xs[m]);
if (mb) { hist.push_back({node, cur}); swap(cur, nw); }
if (l == r) return;
bool lb = nw.eval(xs[l]) < cur.eval(xs[l]);
if (lb != mb) add(nw, node * 2, l, m);
else add(nw, node * 2 + 1, m + 1, r);
}
void add(ll m, ll c) { add({m, c}, 1, 0, n - 1); }
ll query(int idx, int node, int l, int r) {
ll res = tree[node].eval(xs[idx]);
if (l == r) return res;
int m = (l + r) / 2;
if (idx <= m) return min(res, query(idx, node * 2, l, m));
else return min(res, query(idx, node * 2 + 1, m + 1, r));
}
ll query(ll x) {
int idx = lower_bound(xs.begin(), xs.end(), x) - xs.begin();
return query(idx, 1, 0, n - 1);
}
int snapshot() { return hist.size(); }
void rollback(int sz) {
while ((int)hist.size() > sz) {
auto [pos, line] = hist.back(); hist.pop_back();
tree[pos] = line;
}
}
};
struct SegTree {
int n;
vector<vector<pair<ll,ll>>> lines; // (m, c) per node
SegTree(int sz) : n(sz) { lines.resize(4 * sz); }
void add_line(int node, int l, int r, int ql, int qr, ll m, ll c) {
if (ql > r || qr < l) return;
if (ql <= l && r <= qr) { lines[node].push_back({m, c}); return; }
int mid = (l + r) / 2;
add_line(node * 2, l, mid, ql, qr, m, c);
add_line(node * 2 + 1, mid + 1, r, ql, qr, m, c);
}
void add_line(int l, int r, ll m, ll c) { add_line(1, 0, n - 1, l, r, m, c); }
};
int main() {
// Doc du lieu dau vao
ios::sync_with_stdio(false); cin.tie(nullptr);
int Q; cin >> Q;
vector<array<ll, 4>> ops(Q);
vector<ll> qx;
int next_id = 1;
map<int, int> id_to_idx;
map<int, pair<ll,ll>> id_line;
map<int, int> add_time;
for (int i = 0; i < Q; i++) {
int t; cin >> t;
if (t == 1) {
ll a, b; cin >> a >> b;
ops[i] = {1, a, b, next_id};
id_line[next_id] = {a, b};
add_time[next_id] = i;
next_id++;
} else if (t == 2) {
int id; cin >> id;
ops[i] = {2, id, 0, 0};
} else {
ll x; cin >> x;
ops[i] = {3, x, 0, 0};
qx.push_back(x);
}
}
if (qx.empty()) return 0;
SegTree seg(Q);
for (auto &[id, line] : id_line) {
int l = add_time[id];
int r = Q - 1;
for (int i = 0; i < Q; i++) {
if (ops[i][0] == 2 && ops[i][1] == id) { r = i - 1; break; }
}
if (l <= r) seg.add_line(l, r, line.first, line.second);
}
LiChao lichao(qx);
vector<ll> ans(Q, INF);
function<void(int,int,int)> dfs = [&](int node, int l, int r) {
int snap = lichao.snapshot();
for (auto &p : seg.lines[node]) lichao.add(p.first, p.second);
if (l == r) {
if (l < Q && ops[l][0] == 3) ans[l] = lichao.query(ops[l][1]);
} else {
int m = (l + r) / 2;
dfs(node * 2, l, m);
dfs(node * 2 + 1, m + 1, r);
}
lichao.rollback(snap);
};
dfs(1, 0, Q - 1);
for (int i = 0; i < Q; i++) if (ops[i][0] == 3) cout << ans[i] << "\n";
return 0;
}
Nhận xét