CodeVS 4927-线段树练习5
原题
题目描述 Description
有n个数和5种操作:
add a b c:把区间[a,b]内的所有数都增加c;
set a b c:把区间[a,b]内的所有数都设为c;
sum a b:查询区间[a,b]的区间和;
max a b:查询区间[a,b]的最大值;
min a b:查询区间[a,b]的最小值.
输入描述 Input Description
第一行两个整数n,m,第二行n个整数表示这n个数的初始值.
接下来m行操作,同题目描述.
输出描述 Output Description
对于所有的sum、max、min询问,一行输出一个答案
样例输入 Sample Input
10 6
3 9 2 8 1 7 5 0 4 6
add 4 9 4
set 2 6 2
add 3 8 2
sum 2 10
max 1 7
min 3 6
样例输出 Sample Output
49
11
4
数据范围及提示 Data Size & Hint
10%:1<n,m<=10
30%:1<n,m<=10000
100%:1<n,m<=100000
保证中间结果在long long(C/C++)、int64(pascal)范围内
题解
思路不难,可是码农题调起来就是变态,一搞就是一两天...毕竟这是一棵功能完备的线段树.
这题最难的就是add和set两个Lazy标记.
要先明确一点,就是set的Lazy优先级大于add的Lazy,因为不管你前面的操作再多,一个set下去也必须全部清除.
因此:在标记set的Lazy的时候,就要把add的Lazy清除,并更新该节点的set值;在标记add的Lazy的时候,就要判断一下,如果改点已经是set过了的,就要将set加上要add的数,并把改点的add Lazy清零.
在标记set或add的过程中,在子程序的最前端一定要有一个判断,如果路上的搜索到的点所表示的区间已经被set过了,那么一定要先将该点的set下传,并更新该点的sum/max/min值.如果该点没被set过且不是目标区间且del不为0,那么将该点的del下传,并更新该点的sum/max/min值.还有一点很重要,就是一个节点不可能同时存在set的Lazy与add的Lazy.
查询sum/max/min的时候,在子程序的最前端依旧要有一个判断,如果该点所表示的区间已经被set过了,那么一定要先将该点的set下传,并更新该点的sum/max/min值.
这题子程序贼多...但如果没有的话,在下传set或add时就会特别混乱=_=.
下面是代码:
uses math;
var sum,st,del,mx,mn:array[1..800000] of int64;
var a:array[1..100000] of int64;
var en:array[1..800000] of boolean;
var n,m,x,y,z:int64;
var c1,c2,c3,c4:char;
var j,i:longint;
function calcsum(h,l,r:int64):int64;
var m,p:int64;
begin
m:=(l+r)>>1;
if en[h<<1] then p:=st[h<<1]*(m-l+1) else p:=sum[h<<1]+del[h<<1]*(m-l+1);
if en[h<<1+1] then inc(p,st[h<<1+1]*(r-m)) else inc(p,sum[h<<1+1]+del[h<<1+1]*(r-m));
exit(p);
end;
function calcmax(h,l,r:int64):int64;
var p:int64;
begin
if en[h<<1] then p:=st[h<<1] else p:=mx[h<<1]+del[h<<1];
if en[h<<1+1] then p:=max(p,st[h<<1+1]) else p:=max(p,mx[h<<1+1]+del[h<<1+1]);
exit(p);
end;
function calcmin(h,l,r:int64):int64;
var p:int64;
begin
if en[h<<1] then p:=st[h<<1] else p:=mn[h<<1]+del[h<<1];
if en[h<<1+1] then p:=min(p,st[h<<1+1]) else p:=min(p,mn[h<<1+1]+del[h<<1+1]);
exit(p);
end;
procedure build(h,l,r:int64);
var m:int64;
begin
if l=r then begin sum[h]:=a[l];mx[h]:=a[l];mn[h]:=a[l];exit; end;
m:=(l+r)>>1;
build(h<<1,l,m);build(h<<1+1,m+1,r);
sum[h]:=sum[h<<1]+sum[h<<1+1];
mx[h]:=max(mx[h<<1],mx[h<<1+1]);
mn[h]:=min(mn[h<<1],mn[h<<1+1]);
end;
procedure downset(h,l,r:int64);
begin
st[h<<1]:=st[h];st[h<<1+1]:=st[h];
en[h<<1]:=true;en[h<<1+1]:=true;en[h]:=false;
del[h<<1]:=0;del[h<<1+1]:=0;
mx[h]:=st[h];mn[h]:=st[h];sum[h]:=st[h]*(r-l+1);
end;
procedure downdel(h,l,r:int64);
begin
if en[h<<1] then inc(st[h<<1],del[h]) else del[h<<1]:=del[h<<1]+del[h];
if en[h<<1+1] then inc(st[h<<1+1],del[h]) else del[h<<1+1]:=del[h<<1+1]+del[h];
inc(mx[h],del[h]);inc(mn[h],del[h]);inc(sum[h],del[h]*(r-l+1));
del[h]:=0;
end;
procedure add(h,l,r,x,y,c:int64);
var m:int64;
begin
if en[h] then downset(h,l,r);
if (l>=x)and(r<=y) then
begin
del[h]:=del[h]+c;
if en[h] then begin st[h]:=st[h]+c;del[h]:=0; end;
exit;
end;
m:=(l+r)>>1;
if del[h]>0 then downdel(h,l,r);
if x<=m then add(h<<1,l,m,x,y,c);
if y>m then add(h<<1+1,m+1,r,x,y,c);
sum[h]:=calcsum(h,l,r);mx[h]:=calcmax(h,l,r);mn[h]:=calcmin(h,l,r);
end;
procedure setup(h,l,r,x,y,num:int64);
var m,p:int64;
begin
if en[h] then downset(h,l,r);
if (l>=x)and(r<=y) then
begin
st[h]:=num;en[h]:=true;
del[h]:=0;exit;
end;
if del[h]>0 then downdel(h,l,r);
m:=(l+r)>>1;
if x<=m then setup(h<<1,l,m,x,y,num);
if y>m then setup(h<<1+1,m+1,r,x,y,num);
sum[h]:=calcsum(h,l,r);mx[h]:=calcmax(h,l,r);mn[h]:=calcmin(h,l,r);
end;
function asksum(h,l,r,x,y:int64):int64;
var m,p:int64;
begin
if en[h] then downset(h,l,r);p:=0;
if (l>=x)and(r<=y) then if en[h] then p:=p+st[h]*(r-l+1) else p:=p+sum[h]+del[h]*(r-l+1) else
begin
if del[h]>0 then downdel(h,l,r);
m:=(l+r)>>1;
if x<=m then p:=p+asksum(h<<1,l,m,x,y);
if y>m then p:=p+asksum(h<<1+1,m+1,r,x,y);
end;
exit(p);
end;
function askmax(h,l,r,x,y:int64):int64;
var m,p:int64;
begin
p:=-10000000;
if en[h] then downset(h,l,r);
if (l>=x)and(r<=y) then if en[h] then p:=max(p,st[h]) else p:=max(p,mx[h]+del[h]) else
begin
if del[h]>0 then downdel(h,l,r);
m:=(l+r)>>1;
if x<=m then p:=max(p,askmax(h<<1,l,m,x,y));
if y>m then p:=max(p,askmax(h<<1+1,m+1,r,x,y));
end;
exit(p);
end;
function askmin(h,l,r,x,y:int64):int64;
var m,p:int64;
begin
p:=10000000;
if en[h] then downset(h,l,r);
if (l>=x)and(r<=y) then if en[h] then p:=min(p,st[h]) else p:=min(p,mn[h]+del[h]) else
begin
if del[h]>0 then downdel(h,l,r);
m:=(l+r)>>1;
if x<=m then p:=min(p,askmin(h<<1,l,m,x,y));
if y>m then p:=min(p,askmin(h<<1+1,m+1,r,x,y));
end;
exit(p);
end;
begin
readln(n,m);
for i:=1 to n do read(a[i]);readln;
build(1,1,n);
for i:=1 to m do
begin
read(c1,c2,c3,c4);
if c2='e' then begin readln(x,y,z);setup(1,1,n,x,y,z); end;
if c2='d' then begin readln(x,y,z);add(1,1,n,x,y,z); end;
if c2='u' then begin readln(x,y);writeln(asksum(1,1,n,x,y)); end;
if c2='a' then begin readln(x,y);writeln(askmax(1,1,n,x,y)); end;
if c2='i' then begin readln(x,y);writeln(askmin(1,1,n,x,y)); end;
end;
end.