Hướng giải của Đếm cặp đoạn giao nhau
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
Đây là bài toán sweep line kinh điển với các đoạn song song trục toạ độ. Xử lý các sự kiện theo toạ độ \(x\):
- Các đoạn nằm ngang được biểu diễn bằng hai sự kiện (bắt đầu và kết thúc) trong một cấu trúc dữ liệu như BIT/Fenwick trên toạ độ \(y\).
- Các đoạn thẳng đứng: khi gặp, truy vấn xem có bao nhiêu đoạn ngang đang hoạt động trong khoảng \(y\) của nó.
Độ phức tạp: \(O(N \log N)\).
Mã nguồn C++
#include <bits/stdc++.h>
using namespace std;
struct Fenwick {
int n;
vector<int> bit;
Fenwick(int n) : n(n), bit(n + 1, 0) {}
void add(int i, int v) {
for (i++; i <= n; i += i & -i) bit[i] += v;
}
int sum(int i) {
int s = 0;
for (i++; i > 0; i -= i & -i) s += bit[i];
return s;
}
int range(int l, int r) { return sum(r) - sum(l - 1); }
};
struct Event {
int x, y1, y2, type;
Event(int x, int y1, int y2, int type) : x(x), y1(y1), y2(y2), type(type) {}
bool operator<(const Event& o) const {
if (x != o.x) return x < o.x;
return type < o.type;
}
};
int main() {
ios_base::sync_with_stdio(false); cin.tie(NULL);
int n; cin >> n;
vector<Event> events;
vector<int> ys;
for (int i = 0; i < n; i++) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
ys.push_back(y1); ys.push_back(y2);
if (y1 == y2) {
events.emplace_back(x1, y1, y2, 0);
events.emplace_back(x2, y1, y2, 2);
} else {
events.emplace_back(x1, y1, y2, 1);
}
}
sort(ys.begin(), ys.end());
ys.erase(unique(ys.begin(), ys.end()), ys.end());
sort(events.begin(), events.end());
Fenwick ft(ys.size());
long long ans = 0;
for (auto& e : events) {
int y1 = lower_bound(ys.begin(), ys.end(), e.y1) - ys.begin();
int y2 = lower_bound(ys.begin(), ys.end(), e.y2) - ys.begin();
if (e.type == 0) ft.add(y1, 1);
else if (e.type == 2) ft.add(y1, -1);
else ans += ft.range(y1, y2);
}
cout << ans << "\n";
return 0;
}
Nhận xét