1 条题解

  • 0
    @ 2025-10-30 8:56:04

    「IOI2020」装饼干 题解

    问题转化

    我们有 kk 种饼干,类型 ii 的饼干:

    • 每块口味值 = 2i2^i
    • 库存数量 = aia_i
    • 要装 xx 袋饼干,每袋口味值都是 yy

    设每袋中类型 ii 的饼干块数为 tit_i,则:

    • y=i=0k1ti2iy = \sum_{i=0}^{k-1} t_i \cdot 2^i
    • 所有袋子加起来,类型 ii 的饼干总数 = xtiaix \cdot t_i \le a_i
    • 所以 tiai/xt_i \le \lfloor a_i / x \rfloor

    bi=ai/xb_i = \lfloor a_i / x \rfloor,问题转化为:

    有多少个整数 yy 可以表示为 y=i=0k1ti2iy = \sum_{i=0}^{k-1} t_i \cdot 2^i,其中 0tibi0 \le t_i \le b_i


    关键观察

    这是一个有限制的二进制表示问题。如果没有限制 tibit_i \le b_i,那么所有 yy[0,bi2i][0, \sum b_i 2^i] 范围内连续。但由于每个位的"数字" tit_i 有上限 bib_i,会导致一些值无法表示,形成"空洞"。

    我们需要高效地统计所有可表示的 yy 值数量。


    数位 DP 思路

    从低位 (i=0i=0) 到高位 (i=k1i=k-1) 逐位考虑,因为高位依赖于低位的进位。

    状态定义

    dp[i][c]dp[i][c] = 考虑完前 ii 位(位 00i1i-1),向第 ii 位的进位cc 时,已经能形成的不同 yy 的取值数目。

    这里"进位" cc 的含义是:在计算 yy 的二进制表示时,第 i1i-1 位向第 ii 位进位的值。

    状态转移

    当我们处理第 ii 位时:

    • 该位在 yy 的二进制表示中的值 = (c+ti)mod2(c + t_i) \bmod 2
    • 向下一位的进位 = (c+ti)/2\lfloor (c + t_i) / 2 \rfloor

    其中 tit_i 是我们为这一位选择的数字,满足 0tibi0 \le t_i \le b_i

    对于每个可能的 tit_i,我们计算:

    • 新进位 c=(c+ti)/2c' = \lfloor (c + t_i) / 2 \rfloor
    • 当前位值 bit=(c+ti)mod2bit = (c + t_i) \bmod 2

    由于我们是从低位向高位 DP,当前位值一旦确定,就固定了 yy 的这一位,不会改变。

    初始状态

    dp[0][0]=1dp[0][0] = 1(还没有考虑任何位,进位为 0,只有 1 种情况)

    最终答案

    考虑完所有 kk 位后,进位 cc 必须为 0(否则 yy 的值会超出我们考虑的位数范围),所以答案是 dp[k][0]dp[k][0]


    状态数优化

    直接按上述方法 DP,状态数可能很大,因为进位 cc 理论上可以达到 bi\sum b_i,这太大了。

    重要优化观察

    实际上,进位 cc 的范围是有限的。因为:

    • 在第 ii 位,最大可能的 ti=bit_i = b_i
    • 最大进位来自前一位 cmaxc_{max} 加上 bib_i 再除以 2
    • 所以进位大小是 O(maxbi)O(\max b_i) 级别的

    更精确地说,进位 cc 的范围是 [0,maxjibj+1][0, \max_{j \le i} b_j + 1]。由于 bi=ai/xb_i = \lfloor a_i / x \rfloor,且 ai1018a_i \le 10^{18}x1x \ge 1,所以 bi1018b_i \le 10^{18},但实际计算中进位不会太大。

    进一步优化

    我们可以不显式存储所有状态,而是维护一个"可达的进位状态集合"。由于每步进位状态数量不会太多(通常 O(k)O(k)),我们可以用 map 或类似结构存储 (进位,方案数)(进位, 方案数) 对。


    算法步骤

    1. 预处理 bi=ai/xb_i = \lfloor a_i / x \rfloor
    2. 初始化:states = {0: 1}(进位 0 有 1 种方式)
    3. 对于 i=0i = 0k1k-1
      • 创建新状态字典 new_states
      • 对于当前每个状态 (carry, count)
        • 对于 ti=0t_i = 0bib_i
          • 计算总合 = carry+ticarry + t_i
          • 新进位 = 总合/2\lfloor 总合 / 2 \rfloor
          • 当前位值 = 总合mod2总合 \bmod 2(这个用于确定 yy 的唯一性,但 DP 中我们只关心进位)
          • 如果当前位值固定,那么所有导致相同新进位和相同"历史决策"的状态会合并
          • new_states[新进位] += count
      • states = new_states
    4. 最终答案是 states[0](进位为 0 的状态数)

    复杂度分析

    • 外层循环:k60k \le 60
    • 内层状态数:理论上是 O(maxbi)O(\max b_i),但经过优化后实际运行中状态数通常 O(k)O(k)
    • 每个状态考虑 bi+1b_i+1 种选择,但 bib_i 可能很大

    实际实现中,我们不需要枚举 tit_i00bib_i,而是可以数学计算转移,将复杂度优化到 O(kM)O(k \cdot M),其中 MM 是同时存在的状态数。


    总结

    这道题的核心是:

    1. 将问题转化为有限制的二进制表示问题
    2. 使用从低位到高位的数位 DP
    3. 关键状态是"进位"而非具体的 yy
    4. 通过状态合并和数学优化控制复杂度
    • 1

    信息

    ID
    4707
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者