fork download
  1. /*
  2.   BÀI TOÁN: Đếm giao điểm của N đoạn ngang và M đoạn dọc.
  3.   KỸ THUẬT: Sweep Line (Quét) + Fenwick Tree (BIT) + Nén toạ độ.
  4. */
  5.  
  6. #include <iostream>
  7. #include <vector>
  8. #include <algorithm>
  9.  
  10. using namespace std;
  11.  
  12. // --- PHẦN 1: CẤU TRÚC DỮ LIỆU & HẰNG SỐ ---
  13.  
  14. // Định nghĩa thứ tự ưu tiên xử lý khi cùng toạ độ X
  15. // 1. OPEN: Mở đoạn ngang ra (để đoạn dọc sau đó có thể cắt trúng)
  16. // 2. QUERY: Tính toán giao điểm với đoạn dọc
  17. // 3. CLOSE: Đóng đoạn ngang lại (hết tác dụng)
  18. const int OPEN = 1;
  19. const int QUERY = 2;
  20. const int CLOSE = 3;
  21.  
  22. struct Event {
  23. int x, type; // Toạ độ X quét qua, loại sự kiện
  24. int y_low, y_high; // Phạm vi Y (nếu là ngang thì low=high)
  25.  
  26. // Hàm so sánh để sort: X tăng dần -> Type tăng dần
  27. bool operator<(const Event& other) const {
  28. if (x != other.x) return x < other.x;
  29. return type < other.type;
  30. }
  31. };
  32.  
  33. // --- PHẦN 2: FENWICK TREE (Cây BIT) ---
  34. // Dùng để đếm số đoạn ngang đang hoạt động trong khoảng Y
  35.  
  36. vector<int> bit;
  37. int max_val; // Kích thước tối đa của trục Y sau khi nén
  38.  
  39. // Cập nhật: Thêm val vào vị trí idx (val là +1 hoặc -1)
  40. void update(int idx, int val) {
  41. for (; idx <= max_val; idx += idx & -idx) {
  42. bit[idx] += val;
  43. }
  44. }
  45.  
  46. // Lấy tổng: Đếm số lượng từ 1 đến idx
  47. int get(int idx) {
  48. int sum = 0;
  49. for (; idx > 0; idx -= idx & -idx) {
  50. sum += bit[idx];
  51. }
  52. return sum;
  53. }
  54.  
  55. // --- PHẦN 3: NÉN TOẠ ĐỘ (Coordinate Compression) ---
  56. // Giúp xử lý khi toạ độ Y lên tới 1 tỷ, đưa về phạm vi [1...N]
  57.  
  58. void nen_toa_do(vector<Event>& events) {
  59. // 1. Thu thập tất cả các giá trị Y xuất hiện
  60. vector<int> y_values;
  61. for (auto& e : events) {
  62. y_values.push_back(e.y_low);
  63. y_values.push_back(e.y_high);
  64. }
  65.  
  66. // 2. Sắp xếp và lọc trùng
  67. sort(y_values.begin(), y_values.end());
  68. y_values.resize(unique(y_values.begin(), y_values.end()) - y_values.begin());
  69.  
  70. // 3. Thay thế Y cũ bằng số thứ tự (Rank)
  71. for (auto& e : events) {
  72. // Tìm vị trí của y trong mảng đã sort (+1 để chỉ số bắt đầu từ 1 cho BIT)
  73. e.y_low = lower_bound(y_values.begin(), y_values.end(), e.y_low) - y_values.begin() + 1;
  74. e.y_high = lower_bound(y_values.begin(), y_values.end(), e.y_high) - y_values.begin() + 1;
  75. }
  76.  
  77. // Cập nhật kích thước cho BIT
  78. max_val = y_values.size();
  79. bit.assign(max_val + 1, 0); // Khởi tạo BIT toàn số 0
  80. }
  81.  
  82. // --- PHẦN 4: CHƯƠNG TRÌNH CHÍNH (MAIN) ---
  83.  
  84. int main() {
  85. // Tăng tốc độ nhập xuất
  86. ios_base::sync_with_stdio(false); cin.tie(NULL);
  87.  
  88. int n; // Số đoạn ngang
  89. if (!(cin >> n)) return 0;
  90.  
  91. vector<Event> events;
  92.  
  93. // Nhập N đoạn ngang: y cố định, x từ x1 -> x2
  94. for (int i = 0; i < n; i++) {
  95. int x1, x2, y;
  96. cin >> x1 >> x2 >> y;
  97. if (x1 > x2) swap(x1, x2); // Đảm bảo x1 < x2
  98.  
  99. // Tạo 2 sự kiện: Bắt đầu và Kết thúc
  100. events.push_back({x1, OPEN, y, y});
  101. events.push_back({x2, CLOSE, y, y});
  102. }
  103.  
  104. int m; // Số đoạn dọc
  105. cin >> m;
  106. // Nhập M đoạn dọc: x cố định, y từ y1 -> y2
  107. for (int i = 0; i < m; i++) {
  108. int x, y1, y2;
  109. cin >> x >> y1 >> y2;
  110. if (y1 > y2) swap(y1, y2); // Đảm bảo y1 < y2
  111.  
  112. // Tạo 1 sự kiện: Truy vấn
  113. events.push_back({x, QUERY, y1, y2});
  114. }
  115.  
  116. // BƯỚC 1: Nén toạ độ Y (để dùng được BIT)
  117. nen_toa_do(events);
  118.  
  119. // BƯỚC 2: Sắp xếp sự kiện theo trục X (Sweep Line)
  120. sort(events.begin(), events.end());
  121.  
  122. // BƯỚC 3: Quét và xử lý
  123. long long ans = 0; // Biến đếm kết quả (dùng long long vì giao điểm có thể rất nhiều)
  124.  
  125. for (const auto& e : events) {
  126. if (e.type == OPEN) {
  127. // Có thêm 1 đoạn ngang ở độ cao y -> Đánh dấu vào BIT
  128. update(e.y_low, 1);
  129. }
  130. else if (e.type == CLOSE) {
  131. // Đoạn ngang ở độ cao y kết thúc -> Xoá khỏi BIT
  132. update(e.y_low, -1);
  133. }
  134. else { // QUERY
  135. // Đoạn dọc quét từ y_low đến y_high
  136. // Tính xem trong khoảng đó có bao nhiêu đoạn ngang đang active
  137. ans += get(e.y_high) - get(e.y_low - 1);
  138. }
  139. }
  140.  
  141. cout << ans << endl;
  142.  
  143. return 0;
  144. }
  145.  
Success #stdin #stdout 0s 5312KB
stdin
Standard input is empty
stdout
Standard output is empty