Tải bản đầy đủ (.docx) (22 trang)

Chuyên đề cây QUẢN lý đoạn

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (483.29 KB, 22 trang )

HỘI CÁC TRƯỜNG THPT CHUYÊN KHU VỰC
DUYÊN HẢI VÀ ĐỒNG BẰNG BẮC BỘ
….….

HỘI THẢO LẦN THỨ VIII
CHUYÊN ĐỀ

CÂY QUẢN LÝ ĐOẠN

1


CÂY QUẢN LÝ ĐOẠN
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 tố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 tố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. Cấu trúc cây quản lý đoạn
Cây quản lý đoạn là một cây nhị phân đầy đủ (full binary trees) có cấu trúc như sau:


- Mỗi nút quản lý một dãy các đối tượng liên tiếp, trong nút chứa thông tin tổng
hợp từ các đối tượng mà nó quản lý.
- Nút gốc quản lý các đối tượng từ 1 tới n.
- Nếu một nút quản lý dãy các đối tượng từ l tới r (lquản lý các đối tượng từ l tới m và nút con phải của nó quản lý các đối tượng từ
m+1 tới r. Ở đây m = (l+r) div 2.
- Nếu một nút chỉ quản lý một đối tượng thì nó sẽ là nút lá và khơng có nút con.
Trong một số trường hợp cần tăng tốc thuật toán, mỗi đối tượng sẽ được gắn
với một con trỏ leaf[i] trỏ tới nút lá quản lý trực tiếp đối tượng i.

2


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ý.

3. Cài đặt cây quản lý đoạn
Cây quản lý đoạn có 3 thao tác chính:
Build: Xây dựng cây
Update: Cập nhật thơng tin cho đoạn
Query: Truy vấn thông tin của đoạn

Để khảo sát cấu trúc dữ liệu và vận dụng cây quản lý đoạn, ta sét các ví dụ sau.
3.1 Trường hợp biến đổi một phần tử
Ví dụ:
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,v): Đặt ai :=v
- Phép truy vấn Query(i,j): Trả về tổng các phần tử từ ai tới aj
3


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
Outpu
t
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ý.
Xây dựng cây quản lý đoạn
procedure Build(k: longint; l,r: longint);
begin
if ( l>r) then exit;
{nếu k là nút lá}
if ( l = r) then
begin
T[k] := a[l]
exit;
End;
{nếu k không là nút lá}
Build(2*k, l, (l+r) div 2);
4


Build(2*k + 1, (l+r) div 2 + 1, r);
T[k] := T[2 * k] + T[2 * k + 1];
end;
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).
Cập nhật thông tin cho đoạn
Mỗi khi có sự thay đổi giá trị phần tử ta cần phải cập nhật lại cây. Khi một phần
tử ai bị thay đổi, ta nhảy tới nút k là nút lá trực tiếp quản lý ai, thay T[k] = ai và cập

nhật lại thông tin tổng T[ ] cho tất cả các nút chứa phần tử ai trong phạm vi quản lý.
procedure Update(k,l,r: longint; i,v: longint);
var x: longint;
begin
if (l>r) or (l>i) or(rif( l=i) and (r=i) then
begin
T[k]:= v;
Exit;
End;
mid := (l+r) div 2;
Update(2 * k, l, mid, i,v);
Update(2 * k+1,mid+1, r, i,v);
T[k] := T[2*k] + T[2*k+1] ;
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 ừ a i 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
5



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.
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]);
mid := (l+r) div 2;
q1:= Query(2 * k, l, mid, i,j);
q2:= Query(2 * k+1,mid+1, r, i,j);
exit(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).
3.1 Trường hợp biến đổi một dãy phần tử
Trong trường hợp này chúng ta không cập nhật từ nút lá lên nút gốc mà cập
nhật từ nút gốc xuống nút lá. Do đó khi cập nhật thơng tin cho một nút k ta phải cập
nhật thông tin lại cho tất cả các nút là hậu duệ của k mà việc làm này rất tốn thời gian
và ảnh hưởng đến tốc độ của thuật toán.
Để đảm bào thời gian thực hiện thuật toán là tối ưu đối với phép cập nhật khi có
sự biến đổi trên một dãy phần tử thì người ta sử dụng phương pháp truyền lười (Lazy
Propagation). Trong phương pháp truyền lười sử dụng cờ Lazy để ghi nhớ thông tin
cần cập nhật của một nút. Lazy[k] ghi nhớ thông tin cần cập nhật của nút k.
Ý tưởng của phương pháp truyền lười như sau:
Phép toán cập nhật thông tin cho nút k, Update(k):
- Cập nhật thông tin Lazy[k] cho nút k, không cập nhật thông tin Lazy[k]
cho cac nút là hậu duệ của k nhưng phải ghi nhớ thông tin Lazy[k] qua 2
cờ Lazy[2*k] và Lazy[2*k+1] để cập nhật lại cho cac nút là hậu duệ của
k ở phép toán Query.

- Nếu nút k quản lý đoạn nằm trong đoạn cần cập nhật thì cũng chỉ cập
nhật thông tin cho nút k, không cập nhật cho cac nút là hậu duệ của k
nhưng phải ghi nhớ thông tin cập nhật qua 2 cờ Lazy[2*k] và
Lazy[2*k+1] để cập nhật lại cho cac nút là hậu duệ của k ở phép tốn
Query.
Chính vì khơng cập nhật cho cac nút là hậu duệ của k đã làm giảm đáng kể thời
gian thực hiện của phép toán Update.
Phép toán truy vấn thông tin của nút k, Query(k):
- Cập nhật thông tin Lazy[k] cho nút k
6


- Truy vấn thơng tin nút k
Ví dụ:
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
Outpu
t
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
procedure Build(k: longint; l,r: longint);
begin
if ( l>r) then exit;
7


{nếu k là nút lá}
if ( l = r) then
begin
T[k] := a[l]
exit;

End;
{nếu k không là nút lá}
Build(2*k, l, (l+r) div 2);
Build(2*k + 1, (l+r) div 2 + 1, r);
T[k] := (T[2 * k] + T[2 * k + 1]);
end;
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).
Cập nhật thông tin cho đoạn
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]*(r-l +1);
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;
if ( l> r) or(l>j) or(rif (i<=l) and (r<=j) then
begin
t[k]:=t[k]+x*(r-l +1);
if (l<>r) then
begin
lazy[2*k]:= lazy[2*k]+x;
lazy[2*k +1]:= lazy[2*k +1]+x;
end;

exit;
end;
8


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];
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).
Truy vấn thông tin của đoạn
function query(k,L,R:longint; i,j:longint):longint;
var q1,q2,mid:longint;
begin
if (lazy[k]<>0) then
begin
t[k]:=t[k]+lazy[k] *(r-l +1);
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;
if (l>r)or(L>j) or (rif (i<=L) and (r<=j) then exit(t[k]);
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).
4. Bài tập vận dụng
Bài 1. Xếp hàng NKLINEUP
Hàng ngày khi lấy sữa, N con bò của bác John (1 ≤ N ≤ 50000) luôn xếp hàng theo
thứ tự không đổi. Một hôm bác John quyết định tổ chức một trò chơi cho một số con
bò. Để đơn giản, bác John sẽ chọn ra một đoạn liên tiếp các con bò để tham dự trò
chơi. Tuy nhiên để trò chơi diễn ra vui vẻ, các con bò phải không quá chênh lệch về
chiều cao.
9


Bác John đã chuẩn bị một danh sách gồm Q (1 ≤ Q ≤ 200000) đoạn các con bò và
chiều cao của chúng (trong phạm vi [1, 1000000]). Với mỗi đoạn, bác John muốn xác
định chênh lệch chiều cao giữa con bò thấp nhất và cao nhất. Bạn hãy giúp bác John
thực hiện cơng việc này!
Dữ liệu
- Dịng đầu tiên chứa 2 số nguyên N và Q.
- Dòng thứ i trong số N dòng sau chứa 1 số nguyên duy nhất, là độ cao của con
bò thứ i.
- Dòng thứ i trong số Q trong tiếp theo chứa 2 số nguyên A, B (1 ≤ A ≤ B ≤ N),
cho biết đoạn các con bò từ A đến B.
Kết qủa
- Gồm Q dòng, mỗi dòng chứa 1 số nguyên, là chênh lệch chiều cao giữa con
bò thấp nhất và cao nhất thuộc đoạn tương ứng.
Ví dụ
INPU OUTPU
T

T
63
6
1
3
7
0
3
4
2
5
15
46
22
Code chương trình tham khảo
const
fi=''; fo=''; maxn=5*round(1e4)+1; vc=round(1e9);
var
a: array [1..maxn+1] of longint;
Tmax, Tmin: array [1..4*maxn+1] of longint;
n,k: longint; f,g: text;
function max(a,b: longint): longint;
begin
if a>=b then exit(a);
exit(b);
end;
function min(a,b: longint): longint;
10



begin
if a<=b then exit(a);
exit(b)
end;
procedure build(k,l,r: longint);
var m: longint;
begin
if(l>r) then exit;
if l=r then
begin
Tmax[k]:=a[l];
Tmin[k]:=a[l];
exit;
end;
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(rbegin
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);
11


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;
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.
Bài 2. Maximum Sum (KGSS)
Cho dãy a có N phần tử a1, a2, ..., aN ( 0 ≤ ai ≤ 108 , 2 ≤ N ≤ 105).
Cho hai phép biến đổi như sau:
Update
U i x, 1 ≤ i ≤ N, 0 ≤ x ≤ 108: Đặt ai :=x
Query
Q x y, 1 ≤ x < y ≤ N: Tìm hai chỉ số i, j mà x ≤ i < j ≤ y sao cho tổng a[i]
+a[j] là lớn nhất, ghi ra tổng a[i]+a[j]
Input
- Dòng 1 ghi số nguyên N
- Dòng 2 chứa n số nguyên a1, a2, ..., an
- Dòng 3 ghi số nguyên m, m ≤ 105
- m 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, x tương ứng
với phép cập nhật Update
12


+ Nếu ký tự đầu dịng là “Q” thì tiếp theo là hai số nguyên x, y tương
ứng với phép truy vấn Query
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


5
1234
5
6
Q24
Q25
U16
Q15
U17
Q15

7
9
11
12

Code chương trình tham khảo
const
fi=''; fo=''; maxn=round(1e5); vcc=round(1e8)+1;
type
nut= record
g,id: longint;
end;
var
a: array [0..maxn] of longint;
t: array [0..4*maxn] of nut;
n,m: longint; f,g: text;
vc: nut;
function max(a,b: nut): nut;

begin
if a.g>b.g then exit(a)
else exit(b);
end;
procedure Build(x,l,r: longint);
var g: longint;
begin
if l>r then exit;
13


if l=r then
begin
t[k].g:=a[l];
t[k].id:=l;
exit;
end;
g:=(l+r) div 2;
Build(2*k,l,g);
Build(2*k+1,g+1,r);
t[k]:=max(t[2*k],t[2*k+1]);
end;
procedure update(k,l,r:longint; i,x: longint);
var g: longint;
begin
if (l>r) or (r<i) or (l>i) then exit;
if (l=i) and (r=i) then
begin
t[k].g:=x;
exit;

end;
g:=(l+r) div 2;
update(2*k,l,g,i,x);
update(2*x+1,g+1,r,i,x);
t[k]:=max(t[2*k],t[2*k+1]);
end;
function Query(k,l,r:longint; i,j: longint): nut;
var g: longint;
q1,q2:nut;
begin
if (l>r) or (r<i) or (l>j) then exit(vc);
if (i<=l) and (r<=j) then exit(t[k]);
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;
14


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]);

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.
Bài 3. Giá trị lớn nhất (QMAX)
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, mỗi phép 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ị.
Cho q câu hỏi, mỗi câu có dạng (u, v): cho biết phần tử có giá trị lớn nhất thuộc
đoạn [u, v]

Giới hạn
15


n, m, q <= 50000
k>0
Giá trị của một phần tử ln khơng vượt q 231-1
Input
- Dịng 1: n, m
- m dòng tiếp theo, mỗi dòng chứa u, v, k cho biết một phép biến đổi
- Dòng thứ m+2: q
- 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

Code chương trình tham khảo
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];
16


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;
if ( l> r) or(l>j) or(rif (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;
function query(k,L,R:long; i,j:longint):longint;
var q1,q2,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;

17


if (l>r)or(L>j) or (rif (i<=L) and (r<=j) then
exit(t[k]);
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;
procedure xuly;
var kq,i,u,v,h:longint;
begin
assign(f,fi); reset(f);
assign(g,fo); rewrite(g);
readln(f,n,m);
fillchar(lazy,sizeof(lazy),0);
fillchar(t,sizeof(t),0);
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
18


- 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ụ:
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];
19


lazy[2*k +1]:= lazy[2*k +1]+lazy[k];
end;
lazy[k]:=0;
end;
if ( l> r) or(l>j) or(rif (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;
function query(k,L,R:longint; i,j:longint):int64;
var
mid:longint;
q1,q2:int64;
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;
if (l>r)or(L>j) or (r20


if (i<=L) and (r<=j) then

exit(t[k]);
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;
procedure xuly;
var
q, i,u,v,h:longint;
lq:byte;
begin
assign(f,fi); reset(f);
assign(g,fo); rewrite(g);
readln(f,n,q);
fillchar(lazy,sizeof(lazy),0);
fillchar(t,sizeof(t),0);
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.

21


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, ngồ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 tốn tìm kiếm nhị phân, nên sử dụng cây đặc
biệt này q trình thực hiện thuật tốn đạt hiệu quả cao.

22



×