【集训队作业2018】喂鸽子

我的计数还是太差了……

这道题现在知道三种做法。

1. 直接DP

首先显然需要min-max容斥(不知道请百度),不然很难算。

显然对于大小相同的集合答案一样,问题转化为求 \(f_c\)\(c\) 只鸽子最早有一只搞满 \(k\) 个玉米的期望。

然后我就有了一种 \(f_{c,k}\) 表示 \(c\) 只,搞满 \(k\) 颗,直接DP的想法,不知道能不能做。

这道题考虑期望即枚举步数乘上概率 \(\sum_s s \times p(s)\) 比较难搞,但是因为是 \(s\) 次,所以不如考虑一个经典的trick,考虑小于 \(s\) 的,全部贡献。

那么原式 \(\sum_s s \times p(s) = \sum_s p'(s)\),其中 \(p'(s)\) 为大于 \(s\) 步的概率。

考虑 \(p'(s)\),大于等于 \(s\) 步即 \(s\) 步以内里面没有一个饱,枚举里面吃了几个。那么记 \(g_{c,s}\) 为铥了 \(s\) 个,有 \(c\) 只,没有一个饱的概率。

\[p'(s) = \sum_i \binom{s}{i} g_{c,i} \left( \frac{n - c}{n} \right)^{s - i} \]

但是带入计算答案的式子,计算一个 \(c\) 复杂度是 \(O(n^2k^2)\) 的,甚至还要算 \(n\)\(c\)

所以先考虑优化这个式子。第一个想法肯定是卷积。

带上计算答案的式子,对于一个 \(c\),有

\[f_c = \sum_{s = 0} g'(s) = \sum_{s = 0} \sum_i \binom{s}{i} g_{c,i} \left( \frac{n - c}{n} \right)^{s - i} \]

显然可以换元,枚举 \((s - i) + (i) = s\),得到

\[f_c = \sum_i g_{c,i} \sum_j \binom{i + j}{i} \left( \frac{n - c}{n} \right)^{j} \]

显然后者可以通过一些方法算到 \(O(\log n)\) 或者更低。即 \(x = \frac{n - c}{n}\),然后就变成了 \(\left( \frac{1}{1-x} \right)^C\) 这一类的式子。

那么只要能快速计算 \(g_{c,s}\) 就能快速得到 \(f\)

显然 \(g\) 可以通过不断加入一个点来转移,枚举多少步,就是一个组合数卷积(其实EGF可以推的啊)。

\[g_{c,s} = \sum_i \binom{s}{i} g_{c - 1, s - i} \left( \frac{1}{n} \right)^{i} \]

显然表示成 EGF 可以暴力卷积。

复杂度 \(O(n^2 k (\log n + \log k))\)

目前懒得写了。

2. 更好的做法

一个截然不同的做法,直接对题目做。如果把一个鸽子得到的前 \(k\) 个都当做有效的,那么有效的只有 \(nk\) 个。

所以枚举每次有效插入之前多少个饱了的,记这个序列为 \(A\),那么,每要得到一个有效的,期望步数就是加上 \(\frac{n}{rest}\)

但是得到了这个玉米我们怎么知道哪个鸽子会不会饱啊

所以假设玉米扔到了一个等待序列中,等着被安排,直到我们要迅速撑饱一个鸽子的时候组合数取一下玉米,再在剩下的鸽子里钦定一个。注意最后一个玉米是已经确定好的,所以只要选 \(K - 1\) 个。

如果这样做会出问题,在我们搞那个等待序列的时候,就已经多算贡献了。注意到我们的期望步数是选可选集合中的一个,因此那个玉米自带了从属的鸽子,自然会多算。所以要乘上一个 \(\frac{1}{rest}\) 抵消掉这个贡献,以实现加入等待序列。

于是考虑转移,显然状态里记录一下已经放的玉米和已经饱的鸽子个数就可以实现 \(O(1)\) 转移,只要同时记录方案数和期望即可。

复杂度 \(O(n^2k)\)

在奇怪的地方上调了好久。拿阶乘逆来当普通的逆了……

#include <bits/stdc++.h>

const int mod = 998244353;
typedef long long LL;
int mul(int a, int b) { return (LL) a * b % mod; }
void reduce(int & x) { x += x >> 31 & mod; }
const int MAXN = 50010;
int fac[MAXN], inv[MAXN];
int C(int a, int b) { return a < b ? 0 : (LL) fac[a] * inv[b] % mod * inv[a - b] % mod; }

int f[51][MAXN], g[51][MAXN];
int n, K;
int main() {
	fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for (int i = 2; i != MAXN; ++i) {
		fac[i] = mul(fac[i - 1], i);
		inv[i] = mul(inv[mod % i], mod - mod / i);
	}
	for (int i = 2; i != MAXN; ++i)
		inv[i] = mul(inv[i - 1], inv[i]);
	std::cin >> n >> K; const int T = n * K;
	f[0][0] = 1;
	for (int j = 0; j < T; ++j)
		for (int i = 0; i < n; ++i) if (f[i][j]) {
			int c1 = mul(inv[n - i], fac[n - i - 1]), c2 = mul(c1, n);
			reduce(f[i][j + 1] += mul(f[i][j], c1) - mod);
			reduce(g[i][j + 1] += ((LL) f[i][j] * c2 % mod + g[i][j]) * c1 % mod - mod);
			c1 = mul(c1, C(j - i * K, K - 1));
			reduce(f[i + 1][j + 1] += mul(f[i][j], c1) - mod);
			reduce(g[i + 1][j + 1] += ((LL) f[i][j] * c2 % mod + g[i][j]) * c1 % mod - mod);
		}
	std::cout << mul(g[n][T], fac[n]) << std::endl;
	return 0;
}

3. 最通用的做法

还是min-max容斥。但是求下面的期望我们可以直接生成函数。

我们记录下放进钦定的 \(c\) 个鸽子,记有效的玉米为丢个这几个钦定的鸽子的玉米。有效玉米构成了一个序列。显然它们取到 \(s\) 个玉米的期望步数为 \(\frac{sn}{c}\)

对一个长度,实际上只需要求出有贡献的方案数,以及总方案数。总方案数就是 \(\frac{1}{c^s}\),即这个序列所有可能。

对于有贡献的,可以采用指数型生成函数。我们钦定第一个吃饱的是 \(1\),然后最后方案数只要乘上一个 \(c\)

因为最后一颗是确定的,所以只需要知道第一只吃了 \(k - 1\) 个,其他的小于等于 \(k - 1\) 个的方案数。因为是排列,所以使用 EGF

\[\frac{x^{k - 1}}{(k - 1)!} \left(1 + x + \frac{x^2}{2!} + \dots + \frac{x^{k - 1}}{(k - 1)!} \right)^{c - 1} \]

不阻止你们使用Poly Ln EXP,复杂度 \(O(n^2 k (\log n + \log k))\) 。但是为了优美,所以考虑使用推式子技巧。

左边那项乘上特别简单,那么只考虑右边系数的计算。看样子不太好做,所以直接上万能的求导积分等操作,可以方便的求出递推式。

\[\begin{align*} & \frac{\mathrm{d} \left(1 + x + \frac{x^2}{2!} + \dots + \frac{x^{k}}{k!} \right)^c}{\mathrm{dx}} \\ = & c \times \left(1 + x + \frac{x^2}{2!} + \dots + \frac{x^{k}}{k!} \right)^{c - 1} \times \left(1 + x + \frac{x^2}{2!} + \dots + \frac{x^{k - 1}}{(k - 1)!} \right) \end{align*} \]

然后积分一下。但是里面求的是一个卷积,那样的话还是得用FFT。

当然可以优化了。定义

\[f(x) = \left(1 + x + \frac{x^2}{2!} + \dots + \frac{x^{k}}{k!} \right) \]

注意到 \(f'(x) = f(x) - \frac{x^{k}}{k!}\),则

\[\begin{align*} & \frac{\mathrm{d} f^c(x)}{\mathrm{dx}} \\ = & c f^{c-1}(x) \times (f(x) - \frac{x^{k}}{k!}) \\ = & c f^c(x) - \frac{x^{k}}{k!} f^{c-1}(x) \end{align*} \]

对应一下系数,因为积分了,所以 \(x^{n - 1}\) 贡献到 \(x^{n}\),于是有了一个递推式,显然可以 \(O(1)\) 求出一项。

再结合上面讲的,就 \(O(n^2 k)\) 过了此题。

这个做法是从 \(\texttt{@yhx-12243}\) 那里学的,/cy

posted @ 2019-09-26 20:36  daklqw  阅读(567)  评论(0编辑  收藏  举报