1 条题解
-
0
题解
思路
区间模型
第 只鸭子能够被击中的时刻组成闭区间
若 ,这只鸭子在允许射击之前已经飞过原点,必定无法击中。于是问题变为:在非负时间轴上选若干点,相邻两点距离至少为 ,使被这些点刺中的区间数最多。
所有端点和 都是整数。固定一组被刺中的区间后,可行射击时刻只受整数端点约束和形如 的差分约束;若可行,就存在整数顶点解。因此只需考虑整数时刻。
做法
子任务 1:枚举射击集合
时间坐标不超过 。枚举时刻 的全部子集,检查相邻射击是否满足间隔限制,再逐个区间统计是否被刺中。复杂度为 ,其中 。
子任务 2:稠密坐标动态规划
从小到大枚举每个整数时刻 。令 表示已经处理完右端点小于 的区间、最后一次射击位于 时的最少漏掉数量,另设“从未射击”状态。
在 射击时,前一次射击必须满足 ,所以新状态的初值是这些状态的最小值。随后处理所有右端点等于 的区间 :最后射击位置小于 的状态会漏掉它,代价加一;位置至少为 的状态已经击中它。
直接维护所有坐标即可通过第二个子任务。复杂度为 ,这里 。
子任务 3:
每个可击中的区间都含有整数时刻,且不同整数时刻之间的距离至少为 。在每个整数时刻都射击即可击中所有满足 的鸭子。
满分算法:稀疏事件与平衡树
为统一“第一次射击”,把每个可击中区间整体向右平移 ,并在位置 放置一次虚拟射击。原区间变为
虚拟射击位于所有区间左侧,且任意真实射击位置都至少为 ,因此它不会击中鸭子,也不会改变原问题。
定义函数 :只考虑 的区间,并规定最后一次射击位于 时,最少漏掉多少个区间。只需保存 ;为了跳过很长的空坐标段,把最右侧常值段向正无穷延伸。
从 转移到 有两种操作。
-
在 新射击。其前一次射击位置至多为 ,故
-
对每个在 结束的区间 ,所有 的状态都没有射中它,故把函数前缀 整体加一;其余状态不变。
因此需要维护一个分段常值函数,并支持:
- 前缀加一;
- 查询前缀最小值;
- 查询某点所在常值段的值;
- 找到给定位置之后第一个值低于指定值的断点;
- 在最右端加入新断点。
用带懒标记和子树最小值的平衡树保存“断点位置、该段函数值”。以上操作均为 。
不需要逐个扫描最多 个时刻。设当前最右侧函数值为 ,下一时刻对应的前缀右端为 :
- 若 的最小值不等于 ,下一时刻立刻产生新断点;
- 否则,只有当 移到第一个函数值小于 的断点时才会变化,该事件发生在“断点位置 ”;
- 鸭子右端点也是事件,前缀加一后重新计算下一事件。
每只鸭子只产生一次前缀加事件。前缀最小值是非负整数,总增加量不超过 ;由断点进入前缀造成的下降总次数也不超过相应增加次数与初始范围,因此非平凡转移事件总数为 。
复杂度
枚举算法的时间复杂度为 ,空间复杂度为 。稠密坐标动态规划的时间复杂度为 ,空间复杂度为 。满分算法的时间复杂度为 ,空间复杂度为 。
正确性证明
区间模型准确刻画了鸭子与 轴相交的全部允许时刻。虚拟射击既不命中任何平移后的区间,又恰好允许任意非负真实时刻成为第一次射击。
对固定的 ,若在 新射击,所有此前决策只能以某个 结束,取其最小代价恰得新状态;若不在 新射击,则一个右端点为 的区间只有在最后射击 时才被命中,故恰好对 的状态加一。两类转移覆盖所有合法方案且互不遗漏,所以归纳可知 等于定义中的最小漏掉数量。
事件跳跃期间既没有区间结束,移动的前缀最小值也未改变,因此被跳过坐标的新状态与当前最右常值段相同,不会产生新断点;算法只跳过无效转移。最终函数全局最小值就是最少漏掉的可击中鸭子数,用可击中数量减去它即为最大命中数量。
-
- 1
信息
- ID
- 1468
- 时间
- 5000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者