Hướng giải của Hợp Diện Tích Hình Chữ 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.
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ả:
Hướng dẫn giải
Bài toán tính diện tích hợp của các hình chữ nhật bằng thuật toán Sweep Line (Đường quét) kết hợp Segment Tree:
- Ta coi các cạnh dọc của các hình chữ nhật là các sự kiện (events). Một hình chữ nhật bắt đầu tại \(X_1\) và kết thúc tại \(X_2\).
- Ta rời rạc hóa các tọa độ \(Y\) để quản lý bằng Segment Tree.
- Khi đường quét di chuyển dọc theo trục \(X\):
- Sự kiện bắt đầu hình chữ nhật: Cộng 1 vào khoảng \([Y_1, Y_2]\).
- Sự kiện kết thúc hình chữ nhật: Trừ 1 khỏi khoảng \([Y_1, Y_2]\).
- Cây Segment Tree sẽ quản lý độ dài các khoảng được phủ bởi ít nhất một hình chữ nhật (
lentại gốc). - Phần diện tích tăng thêm ở mỗi bước quét bằng:
st.tree[1].len * (events[i+1].x - events[i].x).
Độ phức tạp thời gian: \(O(N \log N)\).
Mã nguồn C++ mẫu
#include <bits/stdc++.h>
using namespace std;
struct Event {
int x; int y1, y2; int val;
bool operator<(const Event& other) const { return x < other.x; }
};
struct Node {
int count; long long len;
};
class SegTree {
public:
int n; vector<Node> tree; vector<int> coords;
SegTree(const vector<int>& c) {
coords = c;
n = coords.size();
tree.assign(4 * n, {0, 0});
}
void update(int node, int start, int end, int l, int r, int val) {
if (l <= start && end <= r) {
tree[node].count += val;
} else {
int mid = (start + end)/2;
if (l < mid) update(2*node, start, mid, l, r, val);
if (r > mid) update(2*node+1, mid, end, l, r, val);
}
if (tree[node].count > 0) {
tree[node].len = coords[end] - coords[start];
} else {
if (start + 1 == end) tree[node].len = 0;
else tree[node].len = tree[2*node].len + tree[2*node+1].len;
}
}
};
int main(){
ios_base::sync_with_stdio(false); cin.tie(NULL);
int n; if (!(cin >> n)) return 0;
vector<Event> events;
vector<int> y_coords;
for (int i=0; i<n; i++) {
int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2;
events.push_back({x1, y1, y2, 1});
events.push_back({x2, y1, y2, -1});
y_coords.push_back(y1);
y_coords.push_back(y2);
}
sort(events.begin(), events.end());
sort(y_coords.begin(), y_coords.end());
y_coords.erase(unique(y_coords.begin(), y_coords.end()), y_coords.end());
int m = y_coords.size();
SegTree st(y_coords);
long long ans = 0;
for (int i=0; i<(int)events.size()-1; i++) {
int y1_idx = lower_bound(y_coords.begin(), y_coords.end(), events[i].y1) - y_coords.begin();
int y2_idx = lower_bound(y_coords.begin(), y_coords.end(), events[i].y2) - y_coords.begin();
st.update(1, 0, m-1, y1_idx, y2_idx, events[i].val);
ans += st.tree[1].len * (events[i+1].x - events[i].x);
}
cout << ans << "\n";
}
Nhận xét