题意:n座楼,初始高度为0.每天修改一栋楼的高度(可增高可降低),问每天修改后在(0,0)能看到多少房屋,当且仅当一栋楼上存在一个高度与(0,0)的连线与之前的楼房不相交时,该楼可见
易知,楼i可见的条件为 :任意 j<i ,j斜率严格小于i的斜率,
所以可见的楼的高度是严格单调递增的(注意并不是LCS)
如果修改一个楼的话,会对所有的楼的答案情况都造成影响,所以它具有合并子问题的性质,那么我们就可以用线段树维护
对于线段是的每一个区间t[x],维护t[x]的最大值max和该区间内满足条件的数
如何合并呢?
对于一个区间t[x],它的答案一定包括左儿子t[2*x]的答案,但同时t[2*x]内的楼房可能会对t[2*x+1]的答案造成影响,所以并不能直接加和
我们把t[2*x+1]继续分为它的左右(l 、 r)儿子两部分来考虑
(1)如果 t[2*x].max>=t[l].max 那么l区间的答案对t[x]已经失效,继续比较t[2*x].max和t[r].max
(2)如果 t[2*x].max<t[l].max 那么r区间内的答案对t[x]依旧有效 ,等于 t[2*x+1].ans-t[l].ans (注意并不是直接是t[r].ans,因为t[r].ans只是针对于t[r]区间说的,而对t[x]做出贡献的是r对原本t[2*x+1]的贡献),然后再去找出t[l]区间内大于t[2*x].max且满足条件的答案数
type
rec=record
l,r,ans:longint;
max:double;
end;
var
n,m,x,y :longint;
i :longint;
z :double;
t :array[0..4000010] of rec;
function maxx(a,b:double):double;
begin
if a<b then exit(b) else exit(a);
end;
function update(maxn:double;x:longint):longint;
var
mid:longint;
begin
if t[x].l=t[x].r then
begin
if maxn>=t[x].max then exit(0) else exit(1);
end;
if maxn>=t[2*x].max then exit(update(maxn,2*x+1)) else
exit(t[x].ans-t[2*x].ans+update(maxn,2*x))
end;
procedure build(x,l,r:longint);
var
mid:longint;
begin
t[x].l:=l; t[x].r:=r;
if l=r then exit;
mid:=(l+r)>>1;
build(2*x,l,mid); build(2*x+1,mid+1,r);
end;
procedure change(x,y:longint;z:double);
var
mid:longint;
begin
if (t[x].l=y) and (t[x].r=y) then
begin
t[x].max:=z;t[x].ans:=1;exit;
end;
mid:=(t[x].l+t[x].r)>>1;
if (y<=mid) then change(2*x,y,z) else change(2*x+1,y,z);
t[x].max:=maxx(t[2*x].max,t[2*x+1].max);
t[x].ans:=t[2*x].ans+update(t[2*x].max,2*x+1);
end;
begin
read(n,m);
build(1,1,n);
for i:=1 to m do
begin
read(x,y);
z:=y/x;
change(1,x,z);
writeln(t[1].ans);
end;
end.
——by Eirlys