AtCoder Grand Contest 038 E: Gachapon

题目传送门:AGC038E

题意简述

\(N\) 个物品,给定 \(A_0 \sim A_{N - 1}\)\(B_0 \sim B_{N - 1}\)

每一个单位时间你会获得一个物品,第 \(i\) 个物品有 \(\dfrac{A_i}{\sum_{j = 0}^{N - 1} A_j}\) 的概率获得。

计算第一次对于每个 \(i\) 都有第 \(i\) 个物品获得至少 \(B_i\) 个的期望时间。

  • \(\displaystyle \sum_{i = 1}^{N - 1} A_i \le 400\)
  • \(\displaystyle \sum_{i = 1}^{N - 1} B_i \le 400\)

题解

\(\displaystyle \Sigma A = \sum_{i = 0}^{N - 1} A_i\) 以及 \(\displaystyle \Sigma B = \sum_{i = 0}^{N - 1} (B_i - 1)\)

使用 Min-Max 容斥,转化为对于每个 \(\{0, 1, \ldots , N - 1\}\) 的非空子集计算第一次子集中的任意一个物品获得至少 \(B_i\) 个的期望时间,如果集合大小为奇数,则对答案贡献 \(1\) 倍,否则贡献 \(-1\) 倍。

也就是满足子集中的所有物品获得的数量都少于 \(B_i\) 个的时刻数量,加上 \(1\)

如果我们把开始的没有任何物品的状态也算作一个时刻,那么 \(+1\) 就省了。

现在考虑对于一个非空子集 \(S \subseteq \{0, 1, \ldots , N - 1\}\) 计算子集中的所有物品获得的数量都少于 \(B_i\) 个的时刻数量(包括初始时刻)。

这也就是对于每一种物品获得的状态,计算这种状态在所有可能情况下出现的期望次数(包括初始时刻),再求和。

我们枚举每一种状态,用 \(x\) 表示,其中 \(x_i\) 表示 \(i\) 获得的个数(\(i \in S\)),必须保证 \(0 \le x_i < B_i\)

接下来需要计算这种状态在所有可能情况下出现的期望次数。

注意到一旦第一次变成了这种状态,只要再次获得 \(S\) 中的物品,状态就会改变。

\(P\) 为一次获得 \(S\) 中的物品的概率,也就是 \(\dfrac{\sum_{i \in S} A_i}{\Sigma A}\)。可以发现一旦第一次变成这种状态之后,状态会期望持续 \(1 / P\) 个时刻(包括第一次的时刻)。对于固定的 \(S\)\(P\) 都是不变的,所以只要求出每一种状态出现的概率再最后乘以 \(1 / P\) 就行了。

考虑计算每一种状态出现的概率,这时就不需要考虑 \(S\) 以外的物品的影响了,它们不会影响出现概率。

那么一种状态的概率就是存在一个获得 \(S\) 中物品的前缀满足 \(i\) 恰好出现了 \(x_i\) 次。

\(\displaystyle X = \sum_{i \in S} x_i\) 以及 \(\displaystyle C = \sum_{i \in S} A_i\),则概率就是 \(\displaystyle X! \prod_{i \in S} {\left( \frac{A_i}{C} \right)}^{x_i} \frac{1}{(x_i)!}\),这些阶乘来自于多重组合数。

利用乘法分配律,以 \(X\) 为 DP 的状态进行转移,我们就可以对于所有可能的状态求出这个概率的和,转移其实类似于指数型生成函数的卷积。

而如果需要对所有的 \(S\) 求和,也是类似的,需要注意处理一下容斥系数。然而对于不同的 \(S\)\(C\) 是不固定的,不过我们可以直接把 \(C\) 提出来,也就是 \(\displaystyle \frac{X!}{C^X} \prod_{i \in S} \frac{A_i^{x_i}}{(x_i)!}\),然后 DP 的时候记录 \(X, C\) 两维状态即可。

这样依次加入 \(N\) 个物品,每次加入的时间复杂度为 \(\mathcal O (B_i X C)\) 进行暴力转移。

下面是代码,时间复杂度为 \(\mathcal O({(\Sigma B)}^2 \Sigma A)\)

#include <cstdio>

typedef long long LL;
const int Mod = 998244353;
const int MN = 405;

inline int qPow(int b, int e) {
	int a = 1;
	for (; e; e >>= 1, b = (LL)b * b % Mod)
		if (e & 1) a = (LL)a * b % Mod;
	return a;
}

int Fac[MN], iFac[MN];
void Init(int N) {
	Fac[0] = 1;
	for (int i = 1; i <= N; ++i) Fac[i] = (LL)Fac[i - 1] * i % Mod;
	iFac[N] = qPow(Fac[N], Mod - 2);
	for (int i = N; i >= 1; --i) iFac[i - 1] = (LL)iFac[i] * i % Mod;
}

int N, A[MN], B[MN], SA, SB;
int f[MN][MN], g[MN][MN], Ans;

int main() {
	scanf("%d", &N);
	for (int i = 1; i <= N; ++i)
		scanf("%d%d", &A[i], &B[i]),
		SA += A[i], SB += B[i] - 1;
	Init(SB);
	SA = SB = 0;
	f[0][0] = Mod - 1;
	for (int i = 1; i <= N; ++i) {
		for (int j = 0; j <= SA + A[i]; ++j)
			for (int k = 0; k <= SB + B[i] - 1; ++k)
				g[j][k] = 0;
		for (int j = 0; j <= SA; ++j) {
			int *G = g[j + A[i]];
			for (int k = 0; k <= SB; ++k)
				for (int x = 0, c = 1; x < B[i]; ++x, c = (LL)c * A[i] % Mod)
					G[k + x] = (G[k + x] + (LL)c * iFac[x] % Mod * f[j][k]) % Mod;
		}
		SA += A[i], SB += B[i] - 1;
		for (int j = 0; j <= SA; ++j)
			for (int k = 0; k <= SB; ++k) {
				f[j][k] -= g[j][k];
				f[j][k] += (f[j][k] >> 31) & Mod;
			}
	}
	for (int j = 1; j <= SA; ++j) {
		int v = qPow(j, Mod - 2);
		int c = (LL)SA * v % Mod;
		for (int k = 0; k <= SB; ++k, c = (LL)c * v % Mod)
			Ans = (Ans + (LL)Fac[k] * c % Mod * f[j][k]) % Mod;
	}
	printf("%d\n", Ans);
	return 0;
}
posted @ 2020-05-06 04:39  粉兔  阅读(680)  评论(0编辑  收藏  举报