1 条题解
-
0
题意理解
我们有 条鱼排成一行,每条鱼有一个大小 。规则是:
- 相邻的两条鱼(中间没有其他鱼)可以互相吞噬。
- 如果 ,那么 可以吃掉 ,之后 变成 。
- 如果大小相等,则可能 吃 ,也可能 吃 。
- 最后只剩一条鱼。
有两种操作:
- 修改某条鱼的大小。
- 查询区间 内,可能成为最后存活者的鱼的数量。
核心难点
- 直接模拟所有可能的吞噬过程不可行,因为组合太多。
- 需要找到一种方法,能快速判断区间中哪些鱼可能“活到最后”。
- 支持单点修改和区间查询, 高达 。
关键观察
1. 吞噬过程的性质
吞噬过程可以看作:鱼不断合并,每次合并是相邻的两条鱼,其中较大者(或相等时任意一方)吸收另一方。
最终存活的那条鱼,其大小等于区间内所有鱼的大小之和 。
2. 必要条件:前缀和与“支配”条件
假设我们考虑鱼 能否存活到最后。
把区间 分成左半 和右半 。
- 对于左半部分,鱼 要能一路往左吞并,必须满足:从 往左的任何一个位置 , 在吞并了 后的总大小,必须大于等于 。
- 类似地,往右吞并也要满足对称条件。
更形式化地,定义:
- 左前缀和 (从 到 的和)
- 右前缀和 (从 到 的和)
鱼 能存活的一个关键条件是:
从 往左看,设 是 的最大值位置,那么鱼 必须能“战胜”这个最大值,即 (实际上需要更精细的条件,因为最大值可能在中间)。
实际上,正确条件是:
- 存在一种吞噬顺序,使得 在遇到左侧任何一条鱼 时, 当前的大小 。
- 这等价于:对于所有 , 不一定成立,但需要更复杂的单调栈式判断。
已知解法思路
这类问题的标准做法是:
1. 转化为“支配”关系
对于区间 ,定义:
- 从 开始往右扫描,维护当前“存活”的鱼的大小,遇到新的鱼时,如果当前鱼大小 新鱼,就吞并;否则当前鱼被替换成新鱼。
- 这样扫描会得到一些“关键点”,这些关键点可能是潜在的最终存活者。
对称地从右往左扫描,也会得到一些关键点。
最终可能存活的鱼,必须是从左扫描和从右扫描都能存活的鱼。
2. 线段树维护
我们可以用线段树,每个节点 存储:
- 区间和
- 从左到右扫描后的“存活”鱼列表(实际上只需要存可能成为候选的极值点)
- 从右到左扫描后的“存活”鱼列表
- 以及这些候选鱼在相反方向扫描时是否能存活的标记信息
合并两个区间时:
- 左子区间的候选鱼,尝试用右子区间的鱼列表去“模拟”吞噬,判断是否能存活。
- 右子区间的候选鱼,尝试用左子区间的鱼列表去“模拟”吞噬,判断是否能存活。
3. 单点修改
单点修改时,更新线段树上对应叶子节点,然后自底向上合并信息。
查询时,获取区间 对应的节点信息,输出其中能双向存活的鱼的数量。
复杂度分析
- 每个线段树节点维护的候选鱼数量是 的(实际上是最多常数个极值点)。
- 合并操作 。
- 总复杂度 。
总结
这道题的核心是:
- 理解吞噬过程的单调性:扫描过程中,只有某些“极大值”鱼可能存活。
- 双向扫描:最终存活者必须能“统治”左侧和右侧。
- 线段树维护候选集:快速合并区间信息,支持修改和查询。
这种“区间可能存活者查询”问题,在 JOI/IOI 中属于高难度数据结构题,需要深刻理解问题性质并设计高效维护方法。
- 1
信息
- ID
- 4257
- 时间
- 4000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 5
- 已通过
- 1
- 上传者