1 条题解
-
0
题意重述
定义一个正整数 是 -美妙 的,如果可以将 的十进制数码组成的可重集分成两个子集 和 ,使得 。
给定 个询问 ,求区间 中 -美妙 数的个数。
,。
第一步:问题转化
的数码可重集 分成两个子集,和分别为 和 ,则 ,差 。
令 ,则差为 ,其中 是某个子集的和。
要存在 使 ,等价于存在 使:
$$\frac{\text{sum}(S) - k}{2} \le t \le \frac{\text{sum}(S) + k}{2}. $$且 必须是 的某个子集和。
结论: 是 -美妙的,当且仅当 存在一个子集和 满足上述不等式。
第二步:子集和的范围
子集和的范围是 到 之间的某些整数。
如果 ,那么显然总可以找到 (比如 或 )使得差值 (因为差最大是 )。
但 是固定的(样例中 只到 ,题里没说 范围,但从样例看 ),而 可以很大(比如很多 加起来),所以主要关心小 情况。
关键观察
对于一个数码集合 ,能得到的子集和是连续的吗?
经典结论:如果 中最大元素为 ,那么子集和能覆盖 中的所有整数,当且仅当 中所有元素和 的某个条件。
更简单地说:如果 中最大的数不超过其余数之和加 ,则子集和是连续的。
实际上,如果我们将数码排序,,若 ,则子集和是 到 的全部整数。但这里数码是 ,有可能不连续。不过对于本题 (从数据范围看,通常 ),我们只需判断是否存在一个子集和 落在 $\left[ \frac{\text{sum}(S)-k}{2}, \frac{\text{sum}(S)+k}{2} \right]$ 内。
由于 很小,这个区间长度是 ,在 很大时,只要子集和覆盖范围足够密,就很可能存在。
第三步:数位 DP 思路
我们需要统计 中满足上述条件的 的个数。
,所以 最多 位十进制。令 表示:
- 当前处理到第 位(从高到低)
- 当前已处理的数码总和为
- 表示 每个数码出现的奇偶性(或者更精确地,记录每个数码出现的次数 mod 2,因为子集划分只关心奇偶性?不,不能只记奇偶,因为可能一个数码出现多次)
- 表示是否紧贴上界
这样状态数太大:,, 要记录 每个数的个数(最多 个),状态爆炸。
第四步:发现小 的简化
因为 ,差值的限制很紧。
考虑两个子集和 的差 ,显然 (因为 ,所以 )。并且 的最小可能值就是 (如果 是偶数,可以做到 ;奇数则至少 )。
因此,如果 ,则不可能。
但 ,所以奇数总和至少差 ,所以 时只能选偶数总和的集合。
第五步:具体计算思路
对于固定 ,判断它是否是 -美妙的:
设 的数码总和为 ,我们看是否存在子集和 满足 。
等价于:是否存在 使得 ,即 靠近 。
由于 很小,我们只需检查 能否取到 附近的几个值。
因为 是整数,所以检查 到 的范围,其中 可能。实际上更简单:
差 意味着 ,所以 。因为 是整数,区间内整数个数最多 个。
因此我们只需判断这个区间内是否有一个整数是 的子集和。
第六步:数位DP设计
状态设计:
,其中 用 位二进制表示 数码的奇偶性?不行,需要具体个数。由于 ,可能的 在 到 之间。
对于给定 ,需要检查的 区间长度 。
我们可以反过来枚举 ,然后判断 是否有子集和等于 。但 也有 到 的可能。
直接做子集和可行性判断:
在数位DP时,我们收集了所有数码的计数 ,然后判断是否存在子集和 在所需区间内。
这是一个 背包可行性问题,但 ,总和 ,可以在状态里存下可达到的子集和集合(bitset 长度 )。状态:,其中 表示当前可达到的子集和集合。
但 长度 不能直接作状态维度。
第七步:观察优化
由于 ,我们不需要知道全部子集和,只需知道是否有一个子集和落在某个长度为 的区间内。
定义 表示是否存在子集和 使得 ,即 是整数且可达。
因为 ,所以只有 个可能的 值。
与 奇偶性相同。所以状态可以设计为: ,同时维护一个 大小的布尔数组,表示在当前已选的数码集合下,对于当前总和 ,可能的 是否存在。
但 随 增加变化, 定义依赖于最终总和 ,我们不能预先知道 。
第八步:另一种思路(数位DP+背包)
我们可以先枚举最终总和 ( 到 ),然后做数位DP统计数码总和恰好为 的数,同时检查是否 -美妙。
检查 -美妙的方法:
对数码集合做子集和DP( 背包),看是否存在 满足 。
这可以在数位DP时用 bitset 维护可达子集和集合,最后判断。由于 ,bitset 长度 ,每个状态存一个 bitset 可行,但状态数: 约 ,每个状态存 位的 bitset(约 字节),总内存约 ,可行。
状态定义: 返回一个 bitset ,其中 表示在当前已选的数码集合中,存在子集和为 。
转移:
当前位选择数字 ,则新的 ,新的 bitset (因为加入数字 后,所有原来的子集和 可以变成 )。初始化:(空集和为 )。
最终对于数 ,总和 ,bitset 为 ,我们检查是否存在 使 ,即检查 $s \in [\lceil (T-k)/2 \rceil, \lfloor (T+k)/2 \rfloor]$ 中是否有 。
第九步:询问处理
有 个询问,我们需要对每个 计算 中 -美妙 数的个数。
如果每个询问单独做数位DP, 状态数 转移 会超时。
注意到 只有 到 可能(从样例看),我们可以 预处理 所有 的答案。
设 表示 中 -美妙 数的个数。
那么询问 答案 = 。我们对每个 ,用数位DP计算 。
最多 , 位,状态 返回 bitset,记忆化。由于 很小,我们可以把 也放进状态吗?不行,因为 是询问参数,我们可以在数位DP结束后根据 判断是否美妙。
更好的办法:
数位DP时不区分 ,只记录 ,然后对于每个 判断是否美妙,累加计数。但这样要同时得到 个 的计数,状态返回时要返回一个数组 ,表示在当前前缀下,继续填数能得到的 -美妙 数个数。
最终方案: 记忆化搜索 返回一个数组 ,其中 表示从当前状态继续填完剩余位,能得到 -美妙 数的个数。
转移时枚举下一位数字,得到新的 和 ,递归调用,把结果累加。
当 时,, 是最终 bitset,我们对于每个 判断是否存在 使 ,是则 ,否则 。
第十步:复杂度
状态数:,约 ,每个状态要返回 个整数的数组,记忆化可行。
总复杂度:状态数 转移 (数字选择) ( 个数),约 ,再乘以询问数 预处理后 回答,可以通过。
总结
本题的关键点:
- 将 -美妙 条件转化为子集和存在性问题。
- 利用 的小范围,只需检查长度为 的区间。
- 使用数位DP配合 bitset 维护子集和可行性。
- 预处理所有 的 ,实现 回答询问。
这样就能在 的大范围内高效处理多个询问。
- 1
信息
- ID
- 5913
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者