1 条题解
-
0
题意理解
我们有 个装置从上到下排列,每个装置有:
- 直径
- 容量
规则:
- 向某个装置注入超过容量的水时,多余的水会向下流到最近的一个直径严格大于当前装置的装置。
- 如果下方没有直径更大的装置,水就流向水路(输出 )。
- 每个询问 互相独立:从装置 注入 升水,问最后停在哪个装置(或水路)。
核心思路
1. 建立水流后继关系
对于每个装置 ,我们需要知道它溢出的水流向哪里。
关键观察:
水会流向在 之后第一个直径大于 的装置。
这可以用 单调栈 在 时间内求出:- 从下到上遍历装置( 到 )
- 维护一个栈,栈内直径递增(栈顶最小)
- 对于装置 ,弹出栈顶所有直径 的装置,那么栈顶就是第一个直径 的装置(即后继)
- 将 压入栈
这样我们得到数组
nxt[i]表示装置 溢出的水流向的装置编号(如果没有,nxt[i] = 0表示流向水路)。
2. 问题转化
从 开始注入 水:
- 如果 ,水全部停在 ,答案就是 。
- 否则,多余的水 流向
nxt[R_i],并继续这个过程。
这形成了一条链:
我们需要沿着这条链走,直到找到第一个装置 ,使得从 流到 的累计多余水量 。
3. 暴力不可行
直接模拟链可能很长,最坏 per query,总复杂度 会超时。
4. 倍增优化
我们预处理:
up[j][i]:从装置 开始,溢出 次后到达的装置编号sum[j][i]:从装置 开始,溢出 次所经过的累计容量(不包括终点容量)
初始化:
up[0][i] = nxt[i]sum[0][i] = C[i](因为第一次溢出需要先填满 的容量)
转移:
up[j][i] = up[j-1][ up[j-1][i] ] sum[j][i] = sum[j-1][i] + sum[j-1][ up[j-1][i] ]注意边界:如果
up[j-1][i] = 0,则up[j][i] = 0,sum[j][i] = sum[j-1][i]。
5. 查询过程
对于查询 :
- 如果 ,直接输出 。
- 否则,令 ,(当前剩余要流出的水量)。
- 从 到 :
- 如果
up[j][curr] != 0且rem > sum[j][curr]:rem -= sum[j][curr]curr = up[j][curr]
- 如果
- 此时再走一步就会停:
- 如果
nxt[curr] = 0,输出 (流向水路) - 否则,如果
rem <= C[nxt[curr]],输出nxt[curr] - 否则,理论上不会发生,因为
rem已经小于等于下一步所需溢出的总容量
- 如果
实际上更简单的写法:
我们直接倍增找到最后一个使得累计容量 的点,然后它的下一个点就是答案。
6. 算法步骤
- 读入数据。
- 单调栈求
nxt[]。 - 预处理倍增数组
up[][]和sum[][]。 - 对每个查询:
- 如果 ,输出 。
- 否则:
- 从高位到低位尝试跳 步:
- 如果
up[j][curr] != 0且sum[j][curr] < rem:
- 如果
- 如果
nxt[curr] = 0,输出 - 否则输出
nxt[curr]
复杂度分析
- 单调栈:
- 倍增预处理:
- 每个查询:
- 总复杂度:,可过 规模。
总结
这道题的关键在于:
- 用单调栈建立溢出关系
- 将问题转化为链上的跳跃查询
- 使用倍增法快速定位最终位置
这种“单调栈建树/链 + 树上倍增”是处理此类问题的经典模式。
- 1
信息
- ID
- 4785
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者