算法复习——LazyTag

《铁路大亨2》貌似已经是一个很古老的游戏了……但是它的古老没有妨碍我们对它的研究。今天我来讲讲怎样用线段树来让我们了解收益情况。

估计在玩这个游戏时大家会有个问题,那就是即使我们设计了一条很好的铁路线,但是我们没法知道我们规划的火车路线能带来的收益是多少,毕竟有货物价格浮动和火车维护费不定等因素存在。线段树+一定的实践可以比较好地解决这个问题。

现在先来假设我们有这么一条铁路,这条铁路实际组成了一条链,再给出我们在n个站点中每个站点售出货物时所能得到的收益。我们再定义两种操作,操作1指把在区间[a,b]内的站点售出货物所能得到的收益增加k,操作2指派出一列火车并使之经过区间[a,b]内的所有站点。

给出m个操作,当执行操作2时输出这列火车能得到的收益。

当然,在正常的游戏中,一张地图内铁路线能经过的站点也就一二百个,而且为了降低维护费我们也不会派出太多列火车,也就是n,m在实际中可能很小,可以直接大力完成。但是如果我们把情况理想化,也就是不计维护费、地图无限大的时候,我们就可以把n,m变得很大,暴力就没法完成了。这时我们就可以用上线段树来帮我们加快速度。(线段树的基础知识出门左转百度去…………)

不过,线段树有时速度也会比较慢,因为普通线段树的行为方式是更新或查询时查到底的,但有时候我们不需要这么做,因为这会带来一些无谓的访问,拖慢程序执行速度。这时lazytag就会派上用场。

Lazytag的行为方式是:

1.     访问某一节点时,清空该节点标记并把此标记下传给它的左右孩子。

2.     当我们所要修改(or查询)的区间[a,b]与该节点所代表的区间相合时,给这一节点作上标记,退出这层递归。

3.     如果不相合,则继续修改(or查询)左右子节点。具体方法是:当[a,b]在此节点所代表区间的左边时访问左子节点,在右边时访问右子节点,否则按如下方式访问:(lchild,a,p^.m)和(rchild,p^.m+1,b)

4.     清空左右子节点,并把左右子节点的值上传至根节点

详细代码如下:

procedure clean(var p:tpoint);
begin
  if p^.tag<>0 then//有标记才要清空啊
  begin
    p^.num:=p^.num+(p^.right-p^.left+1)*p^.tag;//把标记值改为节点值
    if p^.lc<>nil then p^.lc^.tag:=p^.lc^.tag+p^.tag;//标记下传
    if p^.rc<>nil then p^.rc^.tag:=p^.rc^.tag+p^.tag;
    p^.tag:=0;//清空标记
  end;
end;
procedure change(var p:tpoint;a,b,c:longint);
begin
  clean(p);//清空p点标记
  if (a=p^.left)and(b=p^.right) then//区间吻合时打上标记
  begin
    p^.tag:=p^.tag+c;
    exit;
  end;
  if (b<=p^.mid) then//访问下面的节点
  change(p^.lc,a,b,c)
  else
  if a>p^.mid then
  change(p^.rc,a,b,c)
  else
  begin
    change(p^.lc,a,p^.mid,c);
    change(p^.rc,p^.mid+1,b,c);
  end;
  clean(p^.lc);clean(p^.rc);//清空左右子节点标记
  p^.num:=p^.lc^.num+p^.rc^.num;//节点值上传
end;
function query(var p:tpoint;a,b:longint):int64;//原理跟上面一样,我就不打注释了
var ans:int64;
begin
  clean(p);
  ans:=0;
  if (a=p^.left)and(b=p^.right) then
    exit(p^.num);
  if b<=p^.mid then
  ans:=query(p^.lc,a,b)
  else if a>p^.mid then
  ans:=query(p^.rc,a,b)
  else
  ans:=query(p^.lc,a,p^.mid)+query(p^.rc,p^.mid+1,b);
  clean(p^.lc);clean(p^.rc);
  p^.num:=p^.lc^.num+p^.rc^.num;
  exit(ans);
end;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值