Cây Li Chao
Giới thiệu
Luogu 4097 [HEOI2013]Segment
Yêu cầu bảo trì hai thao tác sau trên hệ tọa độ Descartes (bắt buộc trực tuyến):
- Thêm một đoạn thẳng vào mặt phẳng. Ký hiệu đoạn thẳng thứ \(i\) được thêm vào có số hiệu là \(i\), hai đầu mút của đoạn thẳng lần lượt là \((x_0,y_0)\), \((x_1,y_1)\).
- Cho một số \(k\), hỏi trong số các đoạn thẳng cắt đường thẳng \(x = k\), đoạn thẳng nào có tung độ giao điểm lớn nhất (nếu có nhiều đoạn thẳng cùng có tung độ giao điểm lớn nhất, chọn đoạn thẳng có số hiệu nhỏ nhất). Đặc biệt, nếu không có đoạn thẳng nào cắt đường thẳng đã cho, in ra \(0\).
Dữ liệu thỏa mãn: Tổng số thao tác \(1 \leq n \leq 10^5\), \(1 \leq k, x_0, x_1 \leq 39989\), \(1 \leq y_0, y_1 \leq 10^9\).
Chúng ta nhận thấy rằng Segment Tree truyền thống không thể bảo trì tốt thông tin này. Trong trường hợp này, Li Chao Segment Tree (Cây Li Chao) ra đời.
Quá trình
Chúng ta có thể chuyển đổi bài toán thành việc bảo trì các thao tác sau:
- Thêm một hàm bậc nhất, miền xác định là \([l,r]\);
- Cho \(k\), tìm hàm bậc nhất có giá trị lớn nhất tại \(x=k\) trong số tất cả các hàm bậc nhất có miền xác định chứa \(k\). Nếu có nhiều hàm có cùng giá trị lớn nhất, chọn hàm có số hiệu nhỏ nhất.
Lưu ý
Khi đoạn thẳng vuông góc với trục \(x\), sẽ xảy ra trường hợp chia cho 0. Giả sử hai đầu mút của đoạn thẳng lần lượt là \((x,y_0)\) và \((x,y_1)\), \(y_0<y_1\), ta thêm hàm bậc nhất \(f(x)=0\cdot x+y_1\) với miền xác định là \([x,x]\).
Nhìn thấy việc sửa đổi khoảng, chúng ta áp dụng phương pháp giải quyết vấn đề khoảng phổ biến của Segment Tree: gán một nhãn lười (lazy tag) cho mỗi nút. Nhãn lười của mỗi nút \(i\) là một đoạn thẳng, ký hiệu là \(l_i\), biểu thị việc dùng \(l_i\) để cập nhật toàn bộ khoảng mà nút đó đại diện.
Bây giờ chúng ta cần thêm một đoạn thẳng \(f\), xét một khoảng Segment Tree nào đó được đoạn thẳng mới \(f\) bao phủ hoàn toàn. Nếu khoảng này chưa có nhãn, ta đánh dấu trực tiếp bằng đoạn thẳng đó.
Nếu khoảng này đã có nhãn, do việc gộp nhãn rất khó, ta chỉ có thể đẩy nhãn xuống. Tuy nhiên, các nút con cũng có nhãn riêng của chúng và có thể xảy ra xung đột, vì vậy chúng ta phải đẩy nhãn xuống một cách đệ quy.

Như hình vẽ, dựa vào việc giá trị của đoạn thẳng mới \(f\) có lớn hơn nhãn cũ \(g\) hay không, chúng ta có thể chia khoảng hiện tại thành hai khoảng con. Trong đó chắc chắn có một khoảng con được bao phủ hoàn toàn bởi \(f\) hoặc \(g\), nghĩa là trong hai đoạn thẳng, chắc chắn có một đoạn thẳng chỉ có thể trở thành đáp án của khoảng con bên trái, hoặc chỉ có thể trở thành đáp án của khoảng con bên phải. Chúng ta dùng đoạn thẳng này để cập nhật đệ quy cây con tương ứng, và dùng đoạn thẳng còn lại làm nhãn lười để cập nhật toàn bộ khoảng hiện tại. Điều này đảm bảo độ phức tạp của việc đẩy nhãn xuống đệ quy. Khi một đoạn thẳng chỉ có thể là đáp án của khoảng con trái hoặc phải, nó mới được đẩy xuống, vì vậy không cần lo lắng về việc bỏ sót một số đoạn thẳng.
Cụ thể, giả sử trung điểm của khoảng hiện tại là \(m\), ta so sánh giá trị của đoạn thẳng mới \(f\) tại trung điểm với giá trị của đoạn thẳng tối ưu cũ \(g\) tại trung điểm.
Nếu đoạn thẳng mới \(f\) tốt hơn, ta hoán đổi \(f\) và \(g\). Bây giờ hãy xem xét trường hợp \(f\) không tốt bằng \(g\) tại trung điểm:
- Nếu tại đầu mút trái \(f\) tốt hơn, thì \(f\) và \(g\) chắc chắn giao nhau trong nửa khoảng bên trái, \(f\) chỉ có thể tốt hơn \(g\) trong khoảng bên trái, đệ quy xuống con trái để đẩy nhãn;
- Nếu tại đầu mút phải \(f\) tốt hơn, thì \(f\) và \(g\) chắc chắn giao nhau trong nửa khoảng bên phải, \(f\) chỉ có thể tốt hơn \(g\) trong khoảng bên phải, đệ quy xuống con phải để đẩy nhãn;
- Nếu tại cả hai đầu mút \(g\) đều tốt hơn, thì \(f\) không thể trở thành đáp án, không cần tiếp tục đẩy xuống.
Ngoài hai trường hợp trên, còn có một trường hợp là \(f\) và \(g\) giao nhau đúng tại trung điểm, trong quá trình cài đặt chương trình có thể gộp vào trường hợp \(f\) không tốt bằng \(g\) tại trung điểm, kết quả sẽ đệ quy xuống phía đầu mút mà \(f\) tốt hơn.
Cuối cùng, lấy \(g\) làm nhãn lười của khoảng hiện tại.
Đẩy nhãn xuống:
Hiện thực
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Chia nhỏ đoạn thẳng:
Hiện thực
1 2 3 4 5 6 7 8 9 10 | |
Lưu ý nhãn lười không tương đương với đoạn thẳng có giá trị lớn nhất tại trung điểm của khoảng.

Như hình vẽ, sau khi thêm đoạn thẳng màu vàng, chỉ có nhãn của nút màu đỏ được cập nhật, trong khi nhãn của các nút màu xanh lá cây vẫn chưa thay đổi. Nhưng tại trung điểm của khoảng xanh lá cây thứ hai, ba, bốn, rõ ràng đoạn thẳng màu vàng có giá trị lớn nhất.
Khi truy vấn, chúng ta có thể sử dụng tư tưởng "nhãn vĩnh viễn" (marker permanentization), so sánh trong các đoạn thẳng nhãn của tất cả các khoảng Segment Tree chứa \(x\) (không quá \(O(\log n)\) cái) để tìm ra đáp án cuối cùng.
Truy vấn:
Hiện thực
1 2 3 4 5 6 7 8 | |
Theo mô tả trên, độ phức tạp thời gian của quá trình truy vấn rõ ràng là \(O(\log n)\). Trong quá trình chèn, chúng ta cần chia đoạn thẳng ban đầu vào \(O(\log n)\) khoảng, đối với mỗi khoảng, chúng ta lại tốn \(O(\log n)\) thời gian để đẩy nhãn xuống đệ quy, do đó độ phức tạp thời gian của quá trình chèn là \(O(\log^2 n)\).
[HEOI2013]Segment Code tham khảo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | |
Hợp nhất
Tương tự như hợp nhất Segment Tree thông thường, chúng ta định nghĩa quá trình sau để hợp nhất hai nút Li Chao Tree \(u,v\), và lấy \(u\) làm gốc mới.
-
Nếu \(v\) rỗng, kết thúc quá trình.
-
Nếu \(u\) rỗng, gán \(v\) cho \(u\).
-
Chèn đoạn thẳng tương ứng với \(v\) vào cây con có gốc là \(u\).
-
Đệ quy hợp nhất các cây con trái phải tương ứng của \(u,v\).
Nếu hợp nhất một số Li Chao Tree liên quan đến tổng số điểm là \(n\), thì độ phức tạp của quá trình này là \(O(n\log n)\): đối với bất kỳ đoạn thẳng nào tương ứng với một nút trên cây, mỗi lần di chuyển nó, chúng ta hoặc tăng độ sâu của nó lên 1, hoặc xóa trực tiếp khỏi cây, chi phí của hai thao tác này đều là \(O(1)\), mà độ sâu của mỗi điểm tối đa là \(O(\log n)\), vì vậy độ phức tạp như trên.
Hiện thực
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | |
Bài tập
「JSOI2008」Blue Mary mở công ty
Last updated on this page:, Update history
Found an error? Want to help improve? Edit this page on GitHub!
Contributors to this page:OI-wiki
All content on this page is provided under the terms of the CC BY-SA 4.0 and SATA license, additional terms may apply