bzoj 2957 线段树

题意: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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值