NOIP 2012 提高组 借教室(vijos 1782) 线段树85分打法
题目自备,总览中已给链接 ————> 总览链接:http://www.cnblogs.com/qq359084415/p/3371855.html
线段树很短,该程序53行,当然经过丧心病狂的压行可以压到40多行 — — 并且在本题中线段树打法要开很大的数组,
理论上要开4*n的空间才能保证不爆,时效又不高,不提倡,不过可以练手~~~~~~~~~~~~~~~~~~~~~~
var
tag,min:array[0..4000001] of longint; //tag 是记录要下传的值,不多解释;min记录当前区间所有天中供应的最小值
n,z,s,t,d,m:longint; //除 z 外都为题目给出,z为全局变量,记录当前分配的人
<————————————————————————————————————————————————————————>
| 注意:要开范围四倍的数组~其他没什么好说的 |
<————————————————————————————————————————————————————————>
Function small(a,b:longint):longint;
begin
if a>b then small := b
else small := a;
end;
<————————————————————————————————————————————————————————>
| 一个求两数最小值的函数,返还值为小的那个数 |
<————————————————————————————————————————————————————————>
procedure build(o,l,r:longint); //建树,o为当前区间编号,l 为该区间左指针 ,r 为该区间右指针
var m:longint;
begin
if l=r then read(min[o]) else begin //按照递归的先后 ,会按顺序遍历1 ~ n , 所以可以边建树边读取
m:=( l + r ) shr 1;
build(o shl 1 ,l ,m); //递归左区间
build(o shl 1+1,m+1,r); //递归右区间
min[o]:=small(min[o shl 1],min[o shl 1+1]); //由左右两个子树
end;
end;
<————————————————————————————————————————————————————————>
| 一个、一般的、线段树建树、过程,并在、递归过程中、求出、各区间内、供应教室数、最少的那天、供应的教室数。 |
<————————————————————————————————————————————————————————>
Procedure pushdown(o:longint); //下传tag值
begin
inc( tag[o shl 1] , tag[o]); //tag值下传
dec(min[o shl 1] , tag[o]); //将左儿子减去它应减去的tag值
inc( tag[o shl 1+1] , tag[o]); //tag值下传
dec(min[o shl 1+1] , tag[o]); //将右儿子减去它应减去的tag值
tag[o]:=0; //记得清零该点的tag值
end;
<————————————————————————————————————————————————————————>
| 将该编号的 tag 值下传的过程,将、它的、子区间、减去、该子区间、原本该减却暂时没减的 tag 值 (因为 某段区间 |
| 减去、某个要供应的值, 它的子区间也应该减去); |
| 在这,只将该值、传给它的两个子区间就可,没必要做到底,等要用到该子区间时再对该点减去所有应减去的值(即为它 |
| 的tag值) |
<————————————————————————————————————————————————————————>
procedure putout(o,l,r:longint);
var m:longint;
begin
if (l >= s)and(r <= t) then begin //该区间在决策区间内,进行决策
dec(min[o],d); //从减去中减去供应值
inc (tag[o],d); //标记该点
if min[o] < 0 then begin //改点区间出现某天供不应求,通知该人,跳出程序
writeln('-1');
writeln(z);
halt;
end;
exit; //因为该区间已分配,不必继续递归
end;
if tag[o]>0 then pushdown(o); //做到该点时,若 其tag值不为空,代表 它的 两个儿子有 tag 值没减
那么 由于下面要递归它的左右儿子,则 对该点进行 tag 值下传处理
m:=(l + r) shr 1;
if s <= m then putout(o shl 1 ,l ,m); //如果左区间有被覆盖,则递归左区间
if t > m then putout(o shl 1+1,m+1,r); //如果右区间有被覆盖,则递归左区间
min[o]:=small(min[o shl 1],min[o shl 1+1]);//从两个子节点对父节点的最小值更新
end;
<————————————————————————————————————————————————————————>
| 很平常的线段树打法,还是得说 (—> . —>) 时效不高,空间不优, 非常堪忧。。 |
<————————————————————————————————————————————————————————>
begin
readln(n,m);
build(1,1,n);
for z:=1 to m do begin
readln(d,s,t);
putout(1,1,n); //寻找决策区间
end;
writeln(0);
end.
~~~~~~~~~~ END~~~~~~~~~~~