1 条题解
-
0
这份代码的算法设计非常精妙,它巧妙地结合了数论(同余循环节)、组合数学(容斥原理)和动态规划,成功将 级别的超大状态压缩到了可计算的范围内。
为了让你彻底吃透这道题,我将从破局点分析、核心算法推导(4个步骤)以及代码性能优化三个维度,详细解释每一步的思路及其背后的原因。
一、 破局点分析:为什么不能直接 DP?
题目要求统计 位十进制数中,模 余 0 且各位数字之和 的方案数。
- 难点: 高达 ,如果按传统的“数位 DP”逐位决策,状态数将达到 级别,必然超时(TLE)。
- 突破口: 且 非常小。一个数 模 的余数,实际上只取决于每一位的权重 。既然 很小,权重必然存在规律,这就是我们降维打击的关键。
二、 核心算法思路详解(4个步骤)
第一步:权重同余分组(将 压缩为 )
- 思路:计算 的值,将 个位置按照权重余数进行分组。
- 原因:根据鸽巢原理, 的余数最多只有 种,因此必然存在循环节。代码通过
vis数组找出了循环节的起点 和终点 ,并将 个位置分成了 组()。 - 核心意义:同一组内的所有位置,其权重 完全相同。因此,我们完全不需要关心组内每个位置具体填了什么数字,只需要知道这 个位置的数字总和 。这 个位置对总余数的贡献,就等价于 。这一步成功将 个位置压缩成了最多 50 个“组”。
第二步:组内方案数计算(容斥原理)
- 思路:对于第 组(共 个位置),计算其数字之和恰好为 的方案数 。
- 原因:每个位置只能填 ,这等价于求方程 $x_1 + x_2 + \dots + x_{a_i} = j \quad (0 \le x_k \le 9)$ 的整数解个数。直接求带上限的解很困难,但如果没有上限 ,解的个数就是经典的插板法:。
- 核心意义:利用容斥原理,我们可以用无上限的解减去“违规”的解。
- 枚举恰好有 个变量 (即违规)。
- 从 个变量中选 个,方案数为 。
- 将这 个变量预先减去 10,方程变为和为 的无上限插板法,方案数为 。
- 根据容斥原理,奇数个违规减去,偶数个违规加上。代码中的
b[i][j]计算逻辑正是这一公式的完美实现。
第三步:组间动态规划(拼图游戏)
- 思路:定义状态 ,表示考虑前 个组,当前总数字和为 ,总数值模 余数为 的方案数。
- 原因:现在我们知道了每一组内部数字和为 的方案数 ,接下来只需要把这 个组像拼图一样拼起来。
- 核心意义:
- 状态转移时,枚举第 组的数字和 。
- 新的总数字和变为 。
- 新的总余数变为 (因为第 组的权重是 ,数字和为 ,贡献即为 )。
- 转移方程:$f[i][(u + j \times t_i) \bmod p][v + j] \mathrel{+}= f[i-1][u][v] \times b[i][j]$。这一步严谨地维护了总余数和总数字和两个维度。
第四步:前缀和处理(满足 的要求)
- 思路:对最终结果 求前缀和。
- 原因:DP 算出的是数字之和恰好等于 的方案数,而题目要求的是小于等于 。
- 核心意义:通过简单的累加 ,将“恰好等于”转化为“小于等于”,最后直接输出 的结果即可。
三、 原代码的性能缺陷与优化思路
虽然上述数学逻辑无懈可击,但原代码在工程实现上存在一个致命缺陷,导致其在极限数据下会超时(TLE)。
1. 缺陷原因:组合数计算的常数灾难
原代码在计算组合数 时,分子和分母都使用了 的
for循环:for (int i = x - y + 1; i <= x; i++) res = 1ll * res * i % mod; // 分子循环 y 次 for (int i = 1; i <= y; i++) res = 1ll * res * inv[i] % mod; // 分母循环 y 次在计算 时,总调用次数约为 次。每次调用内部循环最多 1000 次,总运算量达到 级别,且每次循环都包含 64 位乘法和取模运算(
% mod)。在 1500ms 的时间限制下,这必然导致 TLE。2. 优化思路:阶乘逆元预处理
- 观察:在公式 中, 可能非常大( 可达 ),但 的最大值仅为 。
- 对策:
- 分子 因为 很大,必须保留 的连乘。
- 但是,分母 是固定的,且 。我们可以在程序开头,花极短的时间预处理 的阶乘逆元,存入数组
invfact。
- 效果:分母的计算从 的循环+取模,变成了 的数组查表。这直接砍掉了一半的运算量和大量的取模操作,使总运行时间从 >1500ms 骤降至 <50ms,完美通过所有测试点。
总结
这份代码的灵魂在于利用同余周期性将 降维到 ,并用容斥原理解决了带限制的组合数计算。只要在组合数计算处加上 的阶乘逆元查表优化,它就是一份逻辑严密、性能达标的满分解答。
- 1
信息
- ID
- 10438
- 时间
- 1500ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 8
- 已通过
- 3
- 上传者