Professional Documents
Culture Documents
Segment trees là một cấu trúc dữ liệu ban đầu được thiết kế cho các ứng dụng
hình học. Cấu trúc này khá phức tạp và được sử dụng để trả lời nhiều loại truy vấn
khó. Segment trees thường được so sánh với interval trees là một dạng cấu trúc dữ
liệu khác cũng cung cấp các chức năng tương đương.
Trong mục này, ta đơn giản hóa cấu trúc segment trees để giải quyết bài toán truy vấn
phạm vi. Điều này làm cho cây quản lý đoạn chỉ giống với segment trees ở hình ảnh
biểu diễn còn các thuộc tính và phương thức trở nên đơn giản và “yếu” hơn nhiều so
với cấu trúc nguyên thủy, theo nghĩa không trả lời được những truy vấn khó như cấu
trúc nguyên thủy.
Trên các diễn đàn thảo luận về thuật toán, đôi khi tên gọi interval trees hoặc segment
trees vẫn được dùng để gọi tên cấu trúc này. Các bài giảng thuật toán cũng chỉ dùng
tên gọi interval trees và segment trees để đề cập tới hai cấu trúc dữ liệu trong hình học
tính toán. Cấu trúc cây quản lý đoạn, chỉ là một hạn chế của interval trees hay
segment trees trong trường hợp cụ thể.
Cấu trúc cây quản lý đoạn cung cấp một cách quản lý mới thông qua các đoạn sơ cấp,
ngoài ra việc cài đặt dễ dàng cũng là một ưu điểm của cấu trúc dữ liệu này.
1
2. Biểu diễn cây quản lý đoạn
Ta biểu diễn cây bằng mảng một chiều. Nút số 1 là nút gốc, nút x không phải là
nút lá sẽ có nút con trái là nút 2x , nút con phải là nút 2x+1 và nút cha là nút x div 2.
Một vấn đề đặt ra là với một đoạn gồm n phần tử (1..n) thì mảng một chiều biểu
diễn cây cần phải có bao nhiêu phần tử là đủ, người ta đã chứng minh chỉ số các nút
trong cây
không vượt quá 4n-3. Do đó để dễ nhớ người ta thường khai báo mảng với các phần
tử được đánh số từ 1 tới 4n.
Ta có nút 1 quản lý các đối tượng từ 1 tới n. Vậy nút 2 quản lý các đối tượng từ
1 tới n div 2, nút 3 quản lý các đối tượng từ n div 2 +1 tới n. Tương tự như vậy với
một nút k nào đó ta sẽ xác định được đoạn mà k quản lý.
2
Yêu cầu: Cho dãy thao tác thực hiện tuần tự, hãy trả lời tất cả các truy vấn
Query
Input
- Dòng 1 chứa 2 số nguyên dương n, k ≤ 105
- Dòng 2 chứa n số nguyên a1, a2, ..., an
- n dòng tiếp, mỗi dòng cho thông tin về một phép biến đổi. Mỗi dòng
bắt đầu bởi một ký tự {U,Q}
+ Nếu ký tự đầu dòng là “U” thì tiếp theo là hai số nguyên i, v
tương ứng với phép cập nhật Update(i,v) (v ≤ 105)
+ Nếu ký tự đầu dòng là “Q” thì tiếp theo là hai số nguyên i, j
tương ứng với phép truy vấn Query(i,j) (i < j)
Output
Trả lời tất cả các truy vấn , với mỗi truy vấn in ra câu trả lời trên 1 dòng
Ví dụ:
Input Output
95 17
1 2 3 4 5 6 7 8 45
9
U15
U36
Q14
U81
Q19
Sử dụng mảng T lưu trữ cây quản lý đoạn. T[k] là tổng giá trị của đoạn mà nút k quản
lý.
Thủ tục khởi tạo cây quản lý đoạn sẽ được thực hiện bằng lời gọi Build(1,1,n).
end;
Thủ tục cập nhật thông tin cho đoạn sẽ được thực hiện bằng lời gọi Update(1,1,n,i,v).
Truy vấn thông tin của đoạn
Phép truy vấ n nhậ n và o hai chỉ số i ≤ j và trả về tổ ng cá c phầ n tử từ ai tớ i aj.
Để truy vấ n về tổ ng cá c phầ n tử từ ai tớ i aj, ta thự c hiện truy vấ n cá c phầ n
tử nằ m trong đoạ n mà nú t k quả n lý. Khi đó ta xét cá c trườ ng hợ p sau:
- Trường hợp 1: [l .. r] [ i .. j] = , nút k không quản lý đối tượng nào trong
phạm vi truy vấn, hàm Query trả về 0.
- Trường hợp 2: [l .. r] [ i .. j], tất cả các đối tượng do nút k quản lý đều nằm
trong phạm vi truy vấn, hàm Query trả về T[k]
- Ngoài hai trường hợp trên, hàm Query gọi đệ quy để “hỏi” hai nút con về
tổng các phần tử truy vấn thuộc phạm vi quản lý của nhánh con trái và tổng các
phần tử truy vấn thuộc phạm vi quản lý của nhánh con phải, sau đó cộng hai kết
quả lại thành kết quả hàm.
4
function Query(k,l,r: longint; i, j: Integer): Int64;
var mid:longint;
q1,q2:int64;
Begin
if (l>r) (j < l ) or ( r < i) then Exit(0);
if (i <= l ) and ( r <=j ) then Exit(T[k]);
Ví dụ:
5
Cho một dãy gồm n số nguyên A=(a1, a2, ..., an).
Xét hai phép biến đổi:
- Phép cập nhật Update(i,j, v): Tăng các phần tử từ ai đến aj thêm giá trị v
- Phép truy vấn Query(i,j): Trả về tổng các phần tử từ ai tới aj
Yêu cầu: Cho dãy thao tác thực hiện tuần tự, hãy trả lời tất cả các truy vấn
Query
Input
- Dòng 1 chứa 2 số nguyên dương n, k ≤ 105
- Dòng 2 chứa n số nguyên a1, a2, ..., an
- n dòng tiếp, mỗi dòng cho thông tin về một phép biến đổi. Mỗi dòng
bắt đầu bởi một ký tự {U,Q}
+ Nếu ký tự đầu dòng là “U” thì tiếp theo là hai số nguyên i, v
tương ứng với phép cập nhật Update(i,v) (v ≤ 105)
+ Nếu ký tự đầu dòng là “Q” thì tiếp theo là hai số nguyên i, j
tương ứng với phép truy vấn Query(i,j) (i < j)
Output
Trả lời tất cả các truy vấn , với mỗi truy vấn in ra câu trả lời trên 1 dòng
Ví dụ:
Input Output
95 32
1 2 3 4 5 6 7 8 75
9
U125
U356
Q14
U891
Q19
Sử dụng mảng T lưu trữ cây quản lý đoạn. T[k] là tổng giá trị của đoạn mà nút k quản
lý.
Sử dụng mảng Lazy ghi nhớ thông tin cần cập nhật. Lazy[k] là thông tin cần cập nhật
của nút k.
Xây dựng cây quản lý đoạn
Thủ tục khởi tạo cây quản lý đoạn sẽ được thực hiện bằng lời gọi Build(1,1,n).
mid:=(l+r) div 2;
update(2*k, l, mid, i, j, x);
update(2*k+1, mid+1,r, i, j, x);
t[k]:= t[2*k] + t[2*k+1];
7
end;
Thủ tục cập nhật thông tin cho đoạn sẽ được thực hiện bằng lời gọi
Update(1,1,n,i,j,x).
mid:=(L+R) div 2;
q1:=query(k*2,L,mid,i,j);
q2:=query(k*2+1,mid+1,r,i,j);
exit(max(q1,q2));
end;
Thủ tục truy vấn thông tin của đoạn sẽ được thực hiện bằng lời gọi Query(1,1,n,i,j).
m:=(l+r) div 2;
build(2*k,l,m);
build(2*k+1,m+1,r);
Tmax[k]:=max(Tmax[2*k],Tmax[2*k+1]);
Tmin[k]:=min(Tmin[2*k],Tmin[2*k+1]);
end;
procedure Query(k,l,r:longint; i,j: longint; var maxx,minn: longint);
var qma1,qma2,qmi1,qmi2, mid:longint;
begin
if(l>r) or (l>j) or(r<i) then
begin
maxx:=-vc;
minn:=vc;
exit;
end;
if ( i<=l) and( r<=j) then
begin
maxx:=Tmax[k];
minn:=Tmin[k];
exit;
end;
mid:=(l+r) div 2;
Query(2*k,l,mid, i,j,qma1,qmi1);
Query(2*k+1,mid+1,r, i,j,qma2,qmi2);
maxx:=max(qma1,qma2);
minn:=min(qmi1,qmi2);
end;
procedure xuly;
var i,j,p,maxx,minn: longint;
10
begin
assign(f,fi); reset(f);
assign(g,fo); rewrite(g);
readln(f,n,k);
for i:=1 to n do readln(f,a[i]);
build(1,1,n);
for p:=1 to k do
begin
readln(f,i,j);
Query(1,1,n,i,j,maxx,minn);
writeln(g,maxx-minn);
end;
close(f); close(g);
end;
BEGIN
Xuly;
END.
11
5 7
12345 9
6 11
Q24 12
Q25
U16
Q15
U17
Q15
g:=(l+r) div 2;
12
Build(2*k,l,g);
Build(2*k+1,g+1,r);
t[k]:=max(t[2*k],t[2*k+1]);
end;
g:=(l+r) div 2;
q1:=Query(2*k,l,g,i,j);
q2:=Query(2*k+1,g+1,r,i,j)
exit(max(q1,q2));
end;
procedure xuly;
var
kq,x,y,i: longint;
c: char;
nhi,nhat,q1,q2:nut;
begin
assign(f,fi); reset(f);
assign(g,fo); rewrite(g);
readln(f,n);
for i:=1 to n do read(f,a[i]);
13
Build(1,1,n);
vc.g:=-vcc;
vc.id:=0;
readln(f,m);
for i:=1 to m do
Begin
readln(f,c,x,y);
if c='U' then update(1,1,n,x,y)
else
begin
nhat:=Query(1,1,n,x,y);
q1:=Query(1,1,n,x,kq.id-1);
q2:=Query(1,1,n,kq.id+1,y);
nhi:=max(q1,q2);
kq:=nhat.g + nhi.g;
writeln(g,kq);
end;
end;
close(f);
close(g);
end;
BEGIN
xuly
END.
14
- q dòng tiếp theo, mỗi dòng chứa u, v cho biết một câu hỏi
Output
Gồm q dòng chứa kết quả tương ứng cho từng câu hỏi.
Ví dụ:
Input Output
62 3
132
463
1
34
CONST
FI=''; FO=''; MAXN=50000;
VAR
A:ARRAY[1..MAXN+1] of longint;
T:array[1..4*maxn] of longint;
Lazy:array[1..4*maxn] of longint;
n,m,p:longint;
f,g:text;
function max(a,b:longint):longint;
begin
if a>b then exit(a) else exit(b);
end;
procedure update(k, l, r:longint; i, j, x:longint);
var mid:longint;
begin
if (lazy[k]<>0) then
begin
t[k]:=t[k]+lazy[k];
if (l<>r) then
begin
lazy[2*k]:= lazy[2*k]+lazy[k];
lazy[2*k +1]:= lazy[2*k +1]+lazy[k];
end;
lazy[k]:=0;
end;
15
if ( l> r) or(l>j) or(r<i) then exit;
if (i<=l) and (r<=j) then
begin
t[k]:=t[k]+x;
if (l<>r) then
begin
lazy[2*k]:= lazy[2*k]+x;
lazy[2*k +1]:= lazy[2*k +1]+x;
end;
exit;
end;
mid:=(l+r) div 2;
update(2*k, l, mid, i, j, x);
update(2*k+1, mid+1,r, i, j, x);
t[k]:= max(t[2*k],t[2*k+1]);
end;
for i:=1 to m do
begin
readln(f,u,v,h);
update(1,1,n,u,v,h);
end;
readln(f,p);
for i:=1 to p do
begin
readln(f,u,v);
kq:=query(1,1,n,u,v);
writeln(g,kq);
end;
close(f);
close(g);
end;
begin
xuly;
end.
Bài 4. Giá trị lớn nhất ver2 (QMAX2)
Cho một dãy gồm n phần tử có giá trị ban đầu bằng 0.
Cho m phép biến đổi và câu hỏi, mỗi phép biến đổi có dạng (u, v, k): tăng mỗi
phần tử từ vị trí u đến vị trí v lên k đơn vị, mỗi câu hỏi có dạng (u, v): cho biết
phần tử có giá trị lớn nhất thuộc đoạn [u, v]
Input
- Dòng 1 chứa 2 số nguyên dương n, m (n <= 50000, m <= 100000)
- m dòng tiếp, mỗi dòng cho thông tin về một phép biến đổi hoặc một câu hỏi.
+ Phép biến đổi có dạng: 0 u v k ( 0< k ≤ 231-1)
+ Câu hỏi có dạng : 1 u v
Output
Gồm m dòng chứa kết quả tương ứng cho từng câu hỏi.
Ví dụ:
17
Input Output
63 4
013
3
046
4
116
CONST
FI=''; FO=''; MAXN=50000;
vc =10000000000000000;
VAR
A:ARRAY[1..MAXN+1] of longint;
T:array[1..4*maxn] of int64;
Lazy:array[1..4*maxn] of int64;
n:longint; kq:int64;
f,g:text;
function max(a,b:int64):int64;
begin
if a>b then exit(a)
else exit(b);
end;
procedure update(k, l, r:longint; i, j, x:longint);
var mid:longint;
begin
if (lazy[k]<>0) then
begin
t[k]:=t[k]+lazy[k];
if (l<>r) then
begin
lazy[2*k]:= lazy[2*k]+lazy[k];
lazy[2*k +1]:= lazy[2*k +1]+lazy[k];
end;
lazy[k]:=0;
end;
mid:=(l+r) div 2;
update(2*k, l, mid, i, j, x);
update(2*k+1, mid+1,r, i, j, x);
t[k]:= max(t[2*k],t[2*k+1]);
end;
for i:=1 to q do
begin
read(f,lq,u,v);
if(lq=0) then
begin
readln(f,h);
update(1,1,n,u,v,h)
end
else
begin
kq:=query(1,1,n,u,v);
writeln(g,kq);
readln(f);
end;
end;
close(f);
close(g);
end;
begin
xuly;
end.
Vậy Cấu trúc cây quản lý đoạn cung cấp một cách quản lý mới thông qua các
đoạn sơ cấp, ngoài ra việc cài đặt dễ dàng cũng là một ưu điểm của cấu trúc dữ liệu
này.
Cây quản lý đoạn dựa trên thuật toán tìm kiếm nhị phân, nên sử dụng cây đặc
biệt này quá trình thực hiện thuật toán đạt hiệu quả cao.
20