1 条题解
-
0
「IOI2020」装饼干 题解
问题转化
我们有 种饼干,类型 的饼干:
- 每块口味值 =
- 库存数量 =
- 要装 袋饼干,每袋口味值都是
设每袋中类型 的饼干块数为 ,则:
- 所有袋子加起来,类型 的饼干总数 =
- 所以
令 ,问题转化为:
有多少个整数 可以表示为 ,其中 。
关键观察
这是一个有限制的二进制表示问题。如果没有限制 ,那么所有 在 范围内连续。但由于每个位的"数字" 有上限 ,会导致一些值无法表示,形成"空洞"。
我们需要高效地统计所有可表示的 值数量。
数位 DP 思路
从低位 () 到高位 () 逐位考虑,因为高位依赖于低位的进位。
状态定义
设 = 考虑完前 位(位 到 ),向第 位的进位为 时,已经能形成的不同 的取值数目。
这里"进位" 的含义是:在计算 的二进制表示时,第 位向第 位进位的值。
状态转移
当我们处理第 位时:
- 该位在 的二进制表示中的值 =
- 向下一位的进位 =
其中 是我们为这一位选择的数字,满足 。
对于每个可能的 ,我们计算:
- 新进位
- 当前位值
由于我们是从低位向高位 DP,当前位值一旦确定,就固定了 的这一位,不会改变。
初始状态
(还没有考虑任何位,进位为 0,只有 1 种情况)
最终答案
考虑完所有 位后,进位 必须为 0(否则 的值会超出我们考虑的位数范围),所以答案是 。
状态数优化
直接按上述方法 DP,状态数可能很大,因为进位 理论上可以达到 ,这太大了。
重要优化观察
实际上,进位 的范围是有限的。因为:
- 在第 位,最大可能的
- 最大进位来自前一位 加上 再除以 2
- 所以进位大小是 级别的
更精确地说,进位 的范围是 。由于 ,且 ,,所以 ,但实际计算中进位不会太大。
进一步优化
我们可以不显式存储所有状态,而是维护一个"可达的进位状态集合"。由于每步进位状态数量不会太多(通常 ),我们可以用 map 或类似结构存储 对。
算法步骤
- 预处理
- 初始化:
states = {0: 1}(进位 0 有 1 种方式) - 对于 到 :
- 创建新状态字典
new_states - 对于当前每个状态
(carry, count):- 对于 到 :
- 计算总合 =
- 新进位 =
- 当前位值 = (这个用于确定 的唯一性,但 DP 中我们只关心进位)
- 如果当前位值固定,那么所有导致相同新进位和相同"历史决策"的状态会合并
- 将
new_states[新进位] += count
- 对于 到 :
states = new_states
- 创建新状态字典
- 最终答案是
states[0](进位为 0 的状态数)
复杂度分析
- 外层循环:
- 内层状态数:理论上是 ,但经过优化后实际运行中状态数通常
- 每个状态考虑 种选择,但 可能很大
实际实现中,我们不需要枚举 从 到 ,而是可以数学计算转移,将复杂度优化到 ,其中 是同时存在的状态数。
总结
这道题的核心是:
- 将问题转化为有限制的二进制表示问题
- 使用从低位到高位的数位 DP
- 关键状态是"进位"而非具体的 值
- 通过状态合并和数学优化控制复杂度
- 1
信息
- ID
- 4707
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者