[CF1716F] Bags with Balls 题解
给一种从组合角度的解法。
首先发现 \(F^k\) 这个条件里的幂次并不是很好看,我们试图构造一种巧妙的结构,在统计这个结构时就可以直接达到统计 \(F^k\) 的效果。
我们定义一个下标的有序 \(k\) 元组 \((i_1,i_2,\cdots,i_k)\),定义对于 \(k\) 元组 \(T\) 的函数 \(f(T)\) 表示下标在 \(k\) 元组中出现过位置的取值都为奇数,其余位置任意,这个 \(k\) 元组中元素可重,满足要求的取法方案数,此时我们发现答案就是 \(\sum_Tf(T)\),因为对于任意一个有 \(F\) 个位置取奇数的情况,我们的 \(k\) 元组中每个元素能取 \(F\) 个位置,方案数正好为 \(F^k\)。
那么问题就在于如何去枚举 \(T\) 来快速求得 \(f(T)\) 的和,首先要解决对于给定的 \(T\) 如何计算 \(f(T)\)。
不难发现 \(f(T)\) 对于 \(T\) 中具体什么元素无关,只与 \(T\) 中有几种元素有关,如果 \(T\) 中有 \(k'\) 种元素,则 \(f(T)=\lceil\frac m2\rceil^{k'}m^{n-k'}\)。
那么枚举 \(T\) 的方式也就很明显了,我们只要枚举 \(k'\),然后计算满足 \(T\) 中不同元素种数为 \(k'\) 的方案数,设方案数为 \(g(k')\)。
那么我们将式子写成如下形式:
那么问题再转化为如何求 \(g(i)\),我们可以用 dp 的思想,设 \(f_{i,j}\) 表示 \(i\) 元组中有 \(j\) 种不同的元素的方案数,可以得到 dp 转移方程:
解释一下转移方程,新的元素有两种选择,与之前的元素相同或者是一种新的元素,分别对应上面的两种转移。
这样我们就得到了一种对于每次询问 \(\mathcal O\left(k^2\right)\) 的做法,基于上面的想法去优化它。
上面的递推式和 \(n\) 相关,故每次得重新递推,如果能将式子转化成两部分,一部分与 \(n\) 无关,预处理,另一个与 \(n\) 有关的部分每次询问用更低的时间复杂度处理,就可以解决了。
那么我们先得从 \(g(i)\) 的意义考虑,怎样去构造一个与 \(n\) 无关的式子与一个部分组合得到 \(g(i)\),首先与 \(k\) 相关是没有关系的,因为 \(k\) 的规模比较小,在 \(\mathcal O\left(k^2\right)\) 范围内的预处理,在调用的时候我们也可以接受在 \(\mathcal O\left(k\right)\) 范围内的枚举,我们基于这个条件去思考这个问题。
前面 \(f_{i,j}\) 的定义是 \(\mathcal O\left(k^2\right)\) 的,我们只要在定义中与 \(n\) 无关就好了,我们发现现在的 \(f_{i,j}\) 的定义中元素的值是 \([1,n]\),实际上种类数只有 \(k\),我们可以考虑用类似离散化的思想,按照元素出现顺序重新标号,询问时在动态分配每个标号对应的元素。
重新定义 \(f_{i,j}\) 表示 \(i\) 元组中出现 \(j\) 种元素,满足对于每个出现的元素 \(i\),\([1,i)\) 中的元素全部在它前面出现,即元素种类无标号,则转移方程:
不难发现这就是斯特林子集数,即第二类斯特林数 \(i\brace j\)。
就像前面说的,接下来我们要用斯特林子集数来得到前面我们的 \(g(i)\),根据 \(g(i)\) 表示有 \(i\) 种不同元素的 \(k\) 元组个数,那么对于 \(i\) 种不同元素,其选取方式一共有 \(k^\underline i\) 种,\(i\) 种不同元素的 \(k\) 元组的无标号方案数为 \(f_{k,i}\),再结合上式,答案就是:
这个式子其实可以直接根据第二类斯特林数的方幂转下降幂得到。
代码就是直接实现这个式子。