循环引理和变态双射题
DeepSkyCore
·
·
题解
这个问题实际上存在一个非常巧妙的双射做法(官方题解),这个做法的思维难度极高,但过程很短,核心代码只有一个两重循环的 DP。
非常可惜的是,现有的中文题解几乎都改编自 jiangly 老师的做法,这个做法仍然优美,但推导过程和实现略复杂。
这个题解详细揭秘了官方题解的精彩双射,希望能帮助大家学习。
在下文中,记一个序列的任意一个不为空的前缀是 非空前缀,记一个长为 n 序列的任意一个长度 \lt n 的前缀是 真前缀。
前置知识:循环引理
给定整数序列 a_1, a_2, \dots, a_n 满足:
- 每个元素 a_i \le 1;
- 总和 \sum_{i = 1}^{n} a_i = S \gt 0。
则在该序列的 n 个循环移位中,恰有 S 个移位满足其 非空前缀和 \gt 0。
对于这个性质的证明,可以考虑其最后一次前缀和达到 1, 2, \dots, S 的 S 个位置,可以证明从它们是符合要求的,这里不再展开。
在下文中,我们需要用到循环引理的一个 等价形式。
等价形式:给定整数序列 a_1, a_2, \dots, a_n 满足:
- 每个元素 a_i \ge -1;
- 总和 \sum_{i = 1}^{n} a_i = -S \lt 0(S \gt 0)。
则在其所有循环移位中,恰有 S 个满足:它可以被 唯一 划分为 S 个 基本块。其中,每个基本块满足两个性质:
证明:把 a 所有元素取相反数,两个限制与循环引理的两个限制相同。
那么,这个序列恰有 S 个循环移位满足其任意 后缀和 \gt 0。
这意味着它的每个 真前缀和 都 \lt S,在这个条件下,我们只需按它第一次到达 1, 2, \dots, S 这些位置来划分即可,其中第一次到达 S 时,前缀长度为 n(即整个前缀)。
再把所有元素取相反数回来,可以得到原结论。
解法
首先,题目中认为数值相同的卡片相同,我们可以通过简单的组合技巧,转化为所有卡片均不同的计数。下文中,我们主要解决的就是这种情况的计数。
题目中,每一次获得硬币都对应一次从 0 出发,最终回到 0 的过程。
每一个过程涉及至少 1 张正数卡片。我们断言,可以为每一枚硬币(每个过程)唯一关联一张正数卡片,称为 主导卡片。
记我们选的主导卡片是 l_1, l_2, \dots, l_k,并记 T = \sum l_i。
记剩下的卡片集合为 R,显然 R 的总和是 -T。
我们定义一个 基本块 是满足下面两个性质的序列:
- 性质 1:总和为 -1。
- 性质 2:任意真前缀和非负。
根据 前置知识,任何一个总和为 -T,且各个元素 \ge -1 的整数序列,它的所有循环移位中 恰有 T 个,使得它能 唯一 分解为 T 个子段,满足每个子段都是基本块。
考虑求出这样的划分,我们看看现在有什么:总和为 T 的主导卡片,以及 T 个基本块。
我们把这些子段再按照主导卡片的值分组,然后把主导卡片插入这些子段中。
这样,一张值为 x 的主导卡片会分配到 x 个子段,这对应了 x + 1 种插入方式。
我们把上面提到的方案统合起来:先枚举 k 个主导卡片集合 R,记 S 是总卡片数(与题面一致),T = \sum_{x \in R} a_x,那么 k 个 0 的序列数就是:
\sum_{R \subset U, |R| = k} \left( \frac{T}{S - k}\prod_{i \in R}(a_i + 1) \right)
有几个细节问题:
- 需要计算主导卡片之外的卡片的排列方案数。这里,这些卡片实际上没有任何限制,只需要其能划分为 T 个基本块,只需要用循环引理处理,这就是为什么需要在前面乘上 \frac{T}{S - k}。另外,还需乘上排列数 (S - k)!;
- 这里枚举的是集合,但实际上主导卡片之间也可以随意排列,所以要乘上 k!;
- 我们计算的是所有卡片均不同的数量,这里需要简单地去重一下。
证明
上面这个方式本质是一个神秘的双射,它构造了一个从原序列到我们的基本块结构的一一映射。只要证明了这个双射的正确性,我们不难验证整个算法的正确性。
结构到序列的映射
根据基本块的性质 1, 2,可以推出性质 3:对于任意 t 基本块拼接在一起的序列,每个真前缀和都应该 \gt -t。
性质 3 同样意味着性质 4:每个非空后缀和 \gt 0。
考虑 t 个基本块,并把一个值为 t 的主导卡片插入其中,我们需要使得插入之后,除了空前缀与全局和,没有一个前缀和 = 0。事实上,假设我们插入 t 并使得序列划分为 [L, t, R] 之后,我们只需把 L 这一部分反转即可。注意,这里的反转是从 卡片 层面反转,而非更大块的 基本块 层面。
证明:如果前缀是在 t 之前截取的,根据性质 4 这个前缀肯定 \gt 0,不满足要求;否则,未截取的后缀肯定 \gt 0,那么截取的前缀肯定 \lt 0,同样不满足要求。
这是一个部分的映射,对于多个部分,只需要分别映射即可。
这样,我们证明了 结构到序列的映射。
序列到结构的映射
首先,通过 k 个 0 的位置,我们把序列划分为 k 部分。
对于每一个部分,我们希望证明它唯一对应上面的那个结构 [L, t, R],其中 t 是主导卡片。换句话说,t 唯一。
类似上文证明的过程,我们不难发现:在 L 这一部分,前缀和从 0 逐渐下降到一个 -x(0 \le x \le t),然后直接提升到 -x + t,然后在 R 的这部分逐步下降到 0,所以,这部分除了开头和结尾,不会出现一个多余的 0。
因此,我们也构造出了 序列到结构的映射。
实现
我们主要计算这个式子:
\sum_{R \subset U, |R| = k} \left( \frac{T}{S - k}\prod_{i \in R}(a_i + 1) \right)
把 S - k 提到外面,一个主要问题是计算 T。
我们把 T 组合意义拆开,拆成 \sum_{i \in R} a_i。我们逐个加入 R,并枚举一个元素为特殊元素,其权值乘上 a_i,那么恰有一个特殊元素的序列权值积,就是处理 T 之后上面这个乘积式的值。
记 dp_{i, 0/1, j} 表示考虑前 i 个元素,选中 j 个,其中 0/1 个特殊元素,我们计算的积和式的值。转移容易做到 O(n^2),同时还可以去掉第一维 i 并原地转移。
事实上还能进一步使用多项式优化,这里不展开。
提交记录