[NOIP2016] 天天爱跑步 (LCA+线段树(动态开点)+差分+dfs序)

本文介绍了NOIP2016中一道题目——天天爱跑步的解题思路,主要涉及LCA(最近公共祖先)、线段树(动态开点)、差分以及dfs序等算法。通过分析问题,提出了利用线段树动态开点存储player的起点,并结合LCA计算观察者的有效观察范围,使用差分避免错误计数。文章详细阐述了向上走和向下走两种情况的处理方法,并强调了线段树动态开点的重要性,以减少内存浪费。
摘要由CSDN通过智能技术生成

[NOIP2016] 天天爱跑步 (LCA+线段树(动态开点)+差分+dfs序)

做完这题,好哈皮,一下子填了好多坑(坑太多= =),说实话这道题我觉得真的很有水平,如果我考的话应该会默默敲暴力
(暴露蒟蒻本性)。。。话不多说来看题吧。
这里写图片描述
这里写图片描述
不得不说,出题人很良心嘛,数据范围给的这么清楚(人家只不过知道你想不出正解罢了)= =
如果按照每个player的路径算观察者肯定是不行的(不信你试试,不过好像有20分(裸暴力))
那我们换一种考虑方式对于每一个观察者(我们假设S为起点,T为终点),S为多少的player才会对他产生贡献(被观察到)。
思考两种情况: 因为是在树上,所以肯定有向上走和向下走两种情况,两种情况是类似的,我们以向上走为例。
不难得出一个式子: 对于i号点的观察员 当存在一个player使得 dep[S]-tim[i]=dep[i] 时,该观察员可以观察到他。
移项可得:dep[S]=tim[i]+dep[i].
观察此式 左边仅与player有关,而右边只与观察者有关,那么我们可以得到这样一个思路
每一个深度建一棵线段树,把所有player的起点插入到以dep[S]为根的线段树中,对于每一个观察者向上走能经过他的,
一定在他的子树中,所以我们在以深度tim[i]+dep[i]为根的线段树中查询他所能控制到的左右区间(dfs序)
但是如果直接这样做是不行的,考虑一种情况,如下图(自己画的,多多见谅)
这里写图片描述
红色的S的确是在观察员i的子树中,如果直接按上文说的查询i的子树,S会被算在内,但明显可以看出S并不会从i经过
怎么办?这就需要差分了(树上差分)在LCA(S,T)的父节点减1,这样就不会把它算在内了,(不理解自己画一画)
这样的话,整体思路就出来了,一遍dfs跑出深度,dfs序,因为要求LCA,倍增又很快,就顺带求出fa
求出每一个player的LCA(S与T的),然后在以深度dep[S]为根的线段树中,S的位置+1,fa[LCA]的位置-1
然后对于每一个节点(即观察员),在以深度tim[i]+dep[i]为根的线段树中查询他所能控制到的左右区间的sum即可

简单说一下向下走
同样可得一个式子:dep[S]+dep[i]-2*dep[LCA(S,T)]=tim[i]
移项可得:dep[S]-2*dep[LCA(S,T)]=tim[i]-dep[i]
同理,在以深度dep[S]-2*dep[LCA(S,T)]为根的线段树中T的位置+1,因为LCA在向上走时考虑过了,所以向下走时,
在LCA的位置-1,对于每一个观察员,在以深度tim[i]-dep[i]为根的线段树中查询他所能控制到的左右区间的sum即可

ATTENTION!!!
有可能dep[S]-2*dep[LCA(S,T)]或tim[i]-dep[i]变成一个负数,所以我们在插入或查询过程中都加上一个较大的正数即可
向上走和向下走 中间 线段树一定要清空!!!
我们再谈一谈线段树的动态开点,由于此题很骚,每个深度都要建一棵线段树,如果像普通的线段树一样,
每个深度都建一棵1-n的线段树,我可以很负责任的告诉你 不MLE才怪!!!
所以我们要动态开点,(普通线段树中

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值