Hướng dẫn giải của Cây Đũa Phép Cơm Nguội
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Hướng tiếp cận
Bài toán yêu cầu xử lý các thao tác trên rừng động (dynamic forest): nối hai cây, tách một cây, truy vấn tổng trên đường đi, và cập nhật giá trị tại một đỉnh. Đây là bài toán kinh điển của Link-Cut Tree (LCT).
Nhận xét quan trọng
- Các thao tác link/cut thay đổi cấu trúc cây liên tục, nên không thể dùng Heavy-Light Decomposition (HLD) hay Euler Tour Tree trên cây tĩnh.
- Link-Cut Tree cho phép thực hiện tất cả 4 thao tác trong thời gian khấu hao (amortized).
- Tổng trên đường đi có thể lên đến , cần dùng
long long.
Thuật toán
Link-Cut Tree (LCT)
LCT biểu diễn rừng bằng tập các Splay Tree (cây nhị phân tự cân bằng). Mỗi đỉnh lưu:
val[x]: giá trị (sức mạnh) tại đỉnhsum[x]: tổng giá trị trong Splay subtree gốc tạich[x][0/1]: con trái/phải trong Splay Treefa[x]: cha trong Splay Tree (hoặc path-parent)rev[x]: cờ đảo ngược (lazy propagation cho make_root)
Các thao tác chính
access(x): Đưa đỉnh lên gốc của Splay Tree chứa toàn bộ đường đi từ đến gốc cây biểu diễn. Sau đó splay lên gốc.
make_root(x): Làm thành gốc cây biểu diễn bằng cách access() rồi đảo ngược đường preferred path (dùng lazy reverse flag).
link(x, y): Nối hai cây - make_root(), đặt cha của là .
cut(x, y): Tách cạnh - - make_root(), access(), ngắt liên kết.
query(x, y): Tổng đường đi - make_root(), access(), trả về sum[] (lúc này Splay Tree gốc chứa đúng đường đi ).
update(x, v): Splay() lên gốc, cập nhật val[] = , push_up.
Splay Tree
Mỗi Splay Tree lưu trữ một preferred path trong cây biểu diễn. Thao tác splay dùng zig-zig/zig-zag rotation để đưa đỉnh lên gốc, với điều kiện dừng khi đỉnh không còn là con (trái/phải) của cha nó trong Splay Tree (tức là đỉnh đã ở gốc của Splay Tree riêng, chỉ còn path-parent pointer).
Độ phức tạp
- Thời gian: khấu hao. Mỗi thao tác access/splay tốn amortized.
- Bộ nhớ: - mỗi đỉnh lưu hằng số biến.
Bình luận