trang chủ / bài tập / elderwand / lời giải

Cây Đũa Phép Cơm Nguội

Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giả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.
Tác giả: huunguyenhuunguyen

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

  1. 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.
  2. Link-Cut Tree cho phép thực hiện tất cả 4 thao tác trong thời gian O(logN) khấu hao (amortized).
  3. Tổng trên đường đi có thể lên đến 105×109=1014, 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 đỉnh x
  • sum[x]: tổng giá trị trong Splay subtree gốc tại x
  • ch[x][0/1]: con trái/phải trong Splay Tree
  • fa[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
  1. access(x): Đưa đỉnh x lên gốc của Splay Tree chứa toàn bộ đường đi từ x đến gốc cây biểu diễn. Sau đó splay x lên gốc.

  2. make_root(x): Làm x thành gốc cây biểu diễn bằng cách access(x) rồi đảo ngược đường preferred path (dùng lazy reverse flag).

  3. link(x, y): Nối hai cây - make_root(x), đặt cha của xy.

  4. cut(x, y): Tách cạnh x-y - make_root(x), access(y), ngắt liên kết.

  5. query(x, y): Tổng đường đi - make_root(x), access(y), trả về sum[y] (lúc này Splay Tree gốc y chứa đúng đường đi xy).

  6. update(x, v): Splay(x) lên gốc, cập nhật val[x] = v, 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: O((N+Q)logN) khấu hao. Mỗi thao tác access/splay tốn O(logN) amortized.
  • Bộ nhớ: O(N) - mỗi đỉnh lưu hằng số biến.

Bình luận

Không có bình luận tại thời điểm này.

gnatmake 12.2.0 a68g 3.1.2 nasm 2.16.1 as_x64 2.46 awk 1.3.4 gcc 16.1.0 csc 6.12.0.200 g++ 16.1.0 g++-themis 16.1.0 g++17 16.1.0 g++20 16.1.0 g++23 16.1.0 clang++ 22.1.6 dmd 2.112.0 dart 3.12.1 gforth 0.7.3 gfortran 12.2.0 go 1.26.3 groovyc 5.0.6 javac 25.0.3 node 26.2.0 kotlinc 2.3.21 sbcl 2.2.9 lua 5.4.8 nim 2.2.10 fpc 3.2.2 fpc-themis 3.2.2 perl 5.36.0 php 8.5.6 pike 8.0 pypy3 7.3.23 python3 3.14.5 racket 8.7 ruby 4.0.5 rustc 1.96.0 csc 5.3.0 ctoj-scratch 0.0.1 sed 4.9 tclsh 8.6 bun 1.3.14 deno 2.8.1 v 0.5.1 zig 0.16.0