Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 11 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
11
Dung lượng
1,03 MB
Nội dung
Heavy-Light Decomposition
Giới thiệu
Kĩ thuật Heavy-Light Decomposition (còn gọi là heavy path decomposition,
từ đây xin viết tắt là HLD) được đưa ra lần đầu năm 1983 bởi Sleator và
Tarjan trong bài báo "A data structure for dynamic trees", Journal of
Computer and System Sciences 26 (3): 362–391 trong phần phân tích tiệm
cận về tính hiệu quả của cấu trúc link/cut tree của họ. Năm 1984, Harel và
Tarjan lại sử dụng một lần nữa kĩ thuật này trong bài báo "Fast
algorithms for finding nearest common ancestors", SIAM Journal on
Computing 13 (2): 338–355 về tìm tổ tiên chung gần nhất của hai nút
trong một cây. HLD sớm chứng tỏ sức mạnh của nó trong nghiên cứu độ
phức tạp của các thuật toán, các cấu trúc dữ liệu trên cây.
Tưởng chừng HLD chỉ có ý nghĩa chủ yếu trên phương diện lý thuyết độ
phức tạp và chỉ là công cụ chứng minh toán học thuần túy cho các bài báo
thì đến những năm 2013-2014, trong các bài toán của nhiều cuộc thi lập
trình online, HLD đã bước ra thực tiễn và nhanh chóng thể hiện được tính
ưu việt của mình trong việc mô tả và xử lí các mối quan hệ động giữa các
đối tượng trong nhiều bài toán, đặc biệt trên cấu trúc cây với các truy vấn
online.
Chúng tôi xin cung cấp một cái nhìn sơ lược về HLD và vài mở rộng của nó
thông qua một số bài toán lập trình thuật toán, ngõ hầu cùng quý vị nắm
bắt kịp với xu hướng thay đổi chóng mặt về giải thuật cũng như cấu trúc
dữ liệu của các cuộc thi lập trình online, các cuộc thi lập trình khu vực và
quốc tế.
HLD trên cây
Cây cân bằng
Ta đã biết cây cân bằng là một cấu trúc tốt trong lập trình. Một cây cân
bằng
nút có độ cao
, điều này cho ta hai tính chất:
Ta cần duyệt qua không quá
nút để đến được nút gốc từ một nút
bất kì trong cây;
Ta cần duyệt qua không quá
nút để di chuyển từ một nút bất
kì đến một nút bất kì khác trong cây.
Hệ số
luôn tốt cho mọi bài toán. Với một cây cân bằng
quyết được một loạt các truy vấn bằng độ phức tạp
nút, ta giải
. Chẳng hạn:
khoảng cách giữa hai nút, trọng số lớn/nhỏ nhất trên một đường đi, tổng
liên tiếp lớn nhất của các đoạn liên tiếp, …
Chuỗi tuyến tính
Một dãy các nút được nối tiếp tuyến tính cũng là một cấu trúc tốt. Ta có thể
xây dựng các segment tree hay binary indexed tree (là các cây cân bằng)
cho chuỗi để giải quyết bằng độ phức tạp
các truy vấn dạng max,
min, tổng đoạn, … trên chuỗi.
Cây không cân bằng
Độ cao của một cây không cân bằng dao động trong một phạm vi quá lớn (
). Với trường hợp suy biến, ta phải duyệt qua
nút để từ một nút
này di chuyển đến một nút khác. Ta xem xét dưới đây cách đối phó với một
cây không cân bằng tương đối đặc biệt.
Bài toán: cho cây như hình vẽ, tính tổng trọng số các nút trên đường đi
(đơn) giữa hai nút bất kì, giả thiết trọng số các nút có thể thay đổi theo
thời gian/truy vấn.
Có thể nhận thấy:
Cây có
nút
Ta cần thăm nút để di chuyển từ đến
Ta thăm ít nhất nút để di chuyển từ đến
Ta thăm ít nhất nút để di chuyển từ đến
Như vậy rõ ràng cần chi phí
để di chuyển giữa hai nút tùy ý trong cây.
Để đối phó, ta thử bẻ cây thành ba chuỗi như hình vẽ:
Với mỗi chuỗi ta có thể xây dựng segment tree cho riêng nó và truy vấn
trên mỗi chuỗi sẽ xuất hiện yếu tố log. Ta nhận thấy:
Cây vẫn có
nút, nhưng đã được phân rã thành ba chuỗi độ dài
thuộc cùng một chuỗi, truy vấn giữa chúng được giải quyết trong
thuộc hai chuỗi khác nhau, đường đi giữa chúng có thể tách
thành
, truy vấn giữa
, chính xác là mất hai lần
được giải quyết trong
Tương tự:
thuộc hai chuỗi khác nhau, đường đi giữa chúng có
thể tách thành
, truy vấn giữa chúng được
giải quyết trong
, chính xác là là mất ba lần
Như vậy, với cây đã cho, bằng phép phân rã thành ba chuỗi và xây dựng
segment tree kèm theo mỗi chuỗi, truy vấn giữa hai nút bất kì tốn chi phí
.
Vấn đề là: cây trên chỉ có đúng hai nút có bậc ba nên ta có được một phân
rã tốt, đường đi giữa hai nút bất kì tách thành không quá ba đoạn chuỗi.
Điều gì xảy ra nếu cây là cây tổng quát? Ta cần một kĩ thuật phân rã phức
tạp hơn nhằm đạt được độ phức tạp chấp nhận được ngay cả trong trường
hợp xấu nhất. Đó chính là HLD.
Phân rã Heavy-Light
Ta sẽ phân rã cây thành các chuỗi rời (không có hai chuỗi nào có nút
chung) theo cách sao cho đường đi từ nút gốc đến một nút bất kì trong cây
phải chuyển qua không quá
chuỗi. Nghĩa là phân rã phải thỏa mãn:
đường đi từ nút gốc đến một nút bất kì có thể tách thành các mảnh, mỗi
mảnh thuộc một chuỗi và có không quá
mảnh.
Nếu thực hiện được phân rã như trên, với hai nút bất kì
đường đi từ
đến
thành hai phần:
đến
;
đến
, ta xét tách
, và
dạng truy vấn chung của chúng là: từ một nút, đi lên một nút tổ tiên của
nó.
Với giả thiết phân rã đã có ta thấy ngay độ phức tạp của một truy vấn sẽ là
Việc còn lại là chỉ ra cách phân rã đó.
Xét một cây tổng quát, ta đặt ra các khái niệm:
Nút con nặng (heavy): trong số các nút con của một nút, nút con ứng
với cây con nặng nhất (nhiều nút nhất) được gọi là nút con nặng, các
nút con còn lại được gọi là nút con nhẹ (light). Mỗi nút trong chỉ có
đúng một nút con nặng.
Cạnh nặng: với mỗi nút trong, cạnh nối nút đó với con nặng của nó
được gọi là một cạnh nặng, các cạnh còn lại được gọi là cạnh nhẹ.
Bằng việc tô màu tất cả các cạnh nặng, ta thu được phân rã HLD, mỗi
chuỗi là một dãy cạnh được tô màu liên thuộc nối tiếp. Do mỗi nút trong
chỉ có đúng một nút con nặng nên các chuỗi là đôi một rời nhau. Hơn thế,
mỗi chuỗi đều là một đường nối một nút đến một tổ tiên của nút đó. Các
cạnh nhẹ đóng vai trò kết nối các chuỗi.
Ta xem xét việc di chuyển từ một nút
con nhẹ , do
theo một cạnh nhẹ xuống một nút
là nút con nhẹ nên cây con gốc
có kích thước không vượt
quá một nửa cây con gốc .
Lại xem xét việc di chuyển từ nút gốc của cây xuống một nút lá, do mỗi lần
đi vào một cạnh nhẹ thì kích thước cây con giảm một nửa nên số cạnh nhẹ
phải qua nhiều nhất là
Vì cạnh nhẹ đóng vai trò kết nối các chuỗi nên
điều đó cũng có nghĩa là phân rã ta vừa xây dựng hoàn toàn thỏa mãn các
yêu cầu đề ra.
Các vấn đề về cài đặt
Để khởi tạo HLD và giải quyết bài toán ta cần hiện thực hóa các công việc:
Tính kích thước cho một cây con gốc
bất kì.
Xác định các cạnh nặng, nút con nặng, qua đó kết nối thành các
chuỗi.
Xây dựng segment tree cho mỗi chuỗi.
Xây dựng sparse table phục vụ bài toán tìm tổ tiên chung gần nhất.
Các thông tin hỗ trợ cần thiết:
Với mỗi nút, cần biết nó thuộc chuỗi nào.
Với mỗi nút, cần biết vị trí của nó trong chuỗi.
Với mỗi chuỗi, cần biết nút đầu tiên của chuỗi (nút tổ tiên).
Với mỗi chuỗi, cần biết độ dài của chuỗi.
Với mỗi truy vấn cập nhật trọng số, cần thực hiện:
Thay đổi trực tiếp trên cạnh nhẹ.
Thay đổi trong segment tree tương ứng với cạnh nặng.
Với mỗi truy vấn hỏi thông tin giữa hai nút
, cần thực hiện:
Xác định
.
Lấy thông tin trên đường
, ta xét tổng quát
:
o Lặp cho đến khi cùng chuỗi : lấy thông tin từ đến đầu chuỗi
chứa , lấy thông tin cạnh nhẹ nối đầu chuỗi chứa
với nút cha
trực tiếp, thay bởi nút cha trực tiếp.
o Khi
cùng chuỗi, lấy thông tin về đoạn
trong chuỗi đó.
o Tổng hợp thông tin.
Toàn bộ các công việc kể trên đều có độ phức tạp
).
Một ứng dụng tư tưởng của HLD
Blocking
Xét bài toán cơ bản: cho dãy
phần tử, thực hiện các truy vấn: cập nhật
giá trị một phần tử nào đó, hỏi thông tin không cộng tính (max, min, …)
của một đoạn phần tử liên tiếp nào đó.
Nhớ lại những gì ta thường làm khi Segment tree, Binary Indexed tree và
các cấu trúc tương tự chưa xuất hiện (chưa biết).
Cách 1. Brute force
Thay đổi trực tiếp phần tử với truy vấn cập nhật. Độ phức tạp
Duyệt qua tất cả các phần tử của đoạn đối với truy vấn hỏi. Độ phức
tạp
.
Cách 2. Blocking (băm khối)
Chia dãy thành
đoạn rời, gọi là các khối, mỗi khối là một đoạn độ dài
. Duy trì dãy thông tin của từng khối. Các truy vấn được xử lí như sau:
Truy vấn cập nhật: cập nhật phần tử, duyệt và cập nhật thông tin
khối chứa phần tử đó. Độ phức tạp
Truy vấn hỏi thông tin đoạn
.
: xác định khối chứa phần tử thứ ,
xác định khối chứa phần tử thứ ; xác định thông tin các khối xen
giữa hai khối vừa tìm được; xác định thông tin nửa khối phải chứa ;
xác định thông tin nửa khối trái chứa ; kết hợp các thông tin. Độ
phức tạp
.
Có thể nhận thấy HLD cũng cùng một tư tưởng xuất phát với kĩ thuật
blocking. Đó là phân rã không gian tìm kiếm thành các mảnh, khối và duy
trì các cấu trúc lưu giữ thông tin, hỗ trợ truy vấn thông tin trên các mảnh,
khối. Điểm đặc biệt của HLD là ta giữ mối liên hệ giữa các khối tốt hơn.
Sau đây ta tìm cách kết hợp hai kĩ thuật này trong một bài toán về tập hợp.
HLD cho tập hợp
Bài toán: cho dãy số
tập
kí hiệu là
và
với
tập chỉ số
. Phần tử của
. Tổng kích thước các tập cỡ
quyết hai loại truy vấn trên dãy :
“? K”: tính tổng tất cả các phần tử của có chỉ số nằm trong tập
. Giải
: gia tăng một lượng
cho tất cả các phần tử của
có chỉ số
nằm trong tập
Hướng giải quyết có thể tóm tắt như sau:
Coi một tập
là nặng nếu
, được đưa vào
nặng được quản lí thông qua vị trí nó được đưa vào
này
, các tập
(vì số
). Các tập còn lại gọi là nhẹ.
Các phần tử của tập nặng được duy trì danh sách những tập nặng nó
có tham gia.
Mỗi tập (cả nặng và nhẹ) đều xác định số phần tử mà nó có chung với
từng tập nặng (nhờ danh sách trên)
Giá trị phần tử được lưu là các giá trị riêng, bỏ qua các lần được
tăng ở tập nặng.
Duy trì mảng tổng của các tập bỏ qua việc tăng đều của các tập nặng,
mảng ghi nhớ lượng tăng đều vào các tập nặng, chúng được gọi tắt
là tổng riêng và lượng tăng.
Xét truy vấn
o Nếu
là tập nặng, gia tăng lượng tăng của , chi phí
o Nếu
là tập nhẹ, thay đổi trực tiếp phần tử trong tập, chi phí
o Tổng riêng của các tập nặng được tăng theo số phần tử chung
với tập , chi phí
Xét truy vấn
.
o Nếu
là tập nặng, kết quả là: tổng riêng
lượng tăng
kích
thước, chi phí
o Nếu
là tập nhẹ, kết quả là: tổng các giá trị riêng
chung với từng tập nặng
số lần
lượng tăng của tập đó. Chi phí của
cả hai pha duyệt phần tử của tập
, duyệt các tập nặng đều là
.
Trong cách giải quyết trên, bằng việc tách rời hoàn toàn các lần tăng trên
mỗi tập nặng, ta đã khéo léo thiết lập mối quan hệ giữa các tập nói chung
với từng tập nặng. Qua đó chấp nhận việc duyệt tăng giá trị riêng của
phần tử trên các tập nhẹ với chi phí chấp nhận được.
Việc kết hợp tiêu chuẩn phân loại Heavy-Light của HLD với cận độ phức
tạp
của blocking đã cho một giải thuật đẹp cho một bài toán khó.
Kết luận
Các giải thuật, cấu trúc dữ liệu, kĩ thuật lập trình mới liên tục nảy sinh theo
thời gian. Bắt kịp với xu hướng đó của cộng đồng lập trình thuật toán trên
thế giới là nhu cầu căn bản của mỗi chúng ta. Qua HLD và một ứng dụng
kết hợp HLD với blocking được trình bày ở trên, có thể thấy rằng: cái mới
nhiều khi chẳng qua chỉ là tìm tòi, phát triển, kết hợp từ nhiều thứ đã rất
cũ kĩ, quen thuộc. Hy vọng rằng cộng đồng chúng ta có thể đào sâu nghiên
cứu và sáng tạo thêm nhiều kết quả mới, đẹp hơn, hiệu quả hơn, đóng góp
vào dòng chảy tiến lên của thế giới lập trình thuật toán.
[...]... trên các tập nhẹ với chi phí chấp nhận được Việc kết hợp tiêu chuẩn phân loại Heavy- Light của HLD với cận độ phức tạp của blocking đã cho một giải thuật đẹp cho một bài toán khó Kết luận Các giải thuật, cấu trúc dữ liệu, kĩ thuật lập trình mới liên tục nảy sinh theo thời gian Bắt kịp với xu hướng đó của cộng đồng lập trình thuật toán trên thế giới là nhu cầu căn bản của mỗi chúng ta Qua HLD và một ứng... trên, có thể thấy rằng: cái mới nhiều khi chẳng qua chỉ là tìm tòi, phát triển, kết hợp từ nhiều thứ đã rất cũ kĩ, quen thuộc Hy vọng rằng cộng đồng chúng ta có thể đào sâu nghiên cứu và sáng tạo thêm nhiều kết quả mới, đẹp hơn, hiệu quả hơn, đóng góp vào dòng chảy tiến lên của thế giới lập trình thuật toán ... nhận Việc kết hợp tiêu chuẩn phân loại Heavy-Light HLD với cận độ phức tạp blocking cho giải thuật đẹp cho toán khó Kết luận Các giải thuật, cấu trúc liệu, kĩ thuật lập trình liên tục nảy sinh theo... thành không ba đoạn chuỗi Điều xảy tổng quát? Ta cần kĩ thuật phân rã phức tạp nhằm đạt độ phức tạp chấp nhận trường hợp xấu Đó HLD Phân rã Heavy-Light Ta phân rã thành chuỗi rời (không có hai... thông tin mảnh, khối Điểm đặc biệt HLD ta giữ mối liên hệ khối tốt Sau ta tìm cách kết hợp hai kĩ thuật toán tập hợp HLD cho tập hợp Bài toán: cho dãy số tập kí hiệu với tập số Phần tử Tổng