CF248E Piglet’s Birthday 题解
题目链接:Piglet’s Birthday
题意:
小猪今天过生日了。他的朋友小熊维尼想要给他最好的礼物 —— 一个蜂蜜罐。当然,小熊明白他不可能把整个罐子都送给小猪。事实上,他很可能会把罐子里的蜂蜜全都吃掉。因此,当小熊计划在路上吃一点小吃时,罐子最开始应该有尽可能多的蜂蜜。
前一天,小熊维尼补充了他的蜂蜜储备。小熊维尼家里有 $n$ 个货架,每个货架上可能有一些,也可能一个蜂蜜罐都没有。在一天中,小熊维尼来到蜂蜜货架上 $q$ 次;在第 $i$ 次来到货架 $u_{i}$,从中取出一些罐子 $k_{i}$,尝试了每个罐子的蜂蜜后,将所有这些罐子放在某个货架 $v_{i}$ 上。当小熊选择罐子时,他遵循自己的直觉。这意味着在货架 $u_{i}$ 上所有 $k_{i}$ 个罐子的集合中,他等概率地选择一个。
现在小熊记得他对蜂蜜罐执行的所有动作。他想要带着前一天没有尝试过的罐子参加聚会。为此,他必须知道没有一罐蜂蜜尝试过的货架数量 $m$ 的数学期望。为了更好地评估自己的机会,小熊维尼希望在执行每个动作后都知道 $m$ 的值。
你的任务是编写一个程序来为他找到这些值。
省流:
给定 $n$ 个货架,初始时每个上面有 $a_i$ 个蜜罐。
有 $q$ 次操作,每次操作形如 $u,v,k$,表示从货架 $u$ 上任意选择 $k$ 个蜜罐试吃(吃过的也还能吃),吃完后把这 $k$ 个蜜罐放到 $v$ 货架上去。
每次操作完之后回答所有蜜罐都被试吃过的货架数量的期望。
输入格式:
输入的第一行包含一个单独的数字 $n$($1 \leq n \leq 10^{5}$)——小熊家的货架数量。第二行包含 $n$ 个整数 $a_{i}$($1 \leq i \leq n$,$0 \leq a_{i} \leq 100$)——第 $i$ 个货架上的蜂蜜罐数量。
接下来一行包含一个整数 $q$($1 \leq q \leq 10^{5}$)——小熊前一天所做动作的数量。然后是 $q$ 行,其中第 $i$ 行描述了按时间顺序发生的一个事件;该行包含三个整数 $u_{i}$,$v_{i}$ 和 $k_{i}$($1 \leq u_{i},v_{i} \leq n$,$1 \leq k_{i} \leq 5$)——小熊从哪个货架取出罐子,小熊尝试了每个罐子后将罐子放在哪个货架上,以及小熊尝试的罐子数量,分别。
货架上的罐子编号从 $1$ 到 $n$。保证小熊维尼从未尝试过从货架上取更多的罐子。
输出格式:
对于每个小熊的动作,打印出该动作执行时的数学期望值 $m$。每个值的相对或绝对误差不得超过 $10^{-9}$。
可以发现,每个架子上没吃过的蜜罐 $🍯$ 永远不会超过 $100$ 个。
记没吃过的蜜罐初始有 $a_i$ 个,并记当前吃过和没吃过的一共有 $b_i$ 个。
设 $f(i,j)$ 表示第 $i$ 个架子上还剩 $j$ 个蜜罐没吃过的概率。(老套路了,期望转概率计算)
考虑转移,对于操作 $(u,v,w)$ ,有
注意这道题没有限制 $b_i$ 的大小,因此组合数的数组要开到 C[500000 + 15][5 + 5]
,并使用 double
。
时间复杂度 $\mathcal{O}(500\times q)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e5 + 15))
#define M ((int)(5e5 + 15))
int a[N], now[N];
double res, f[N][114], C[M][14];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cout << fixed << setprecision(10);
int n, q; cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i]; now[i] = a[i];
f[i][a[i]] = 1; res += f[i][0];
}
for(int i = 0; i < M - 5; i++) C[i][0] = 1;
for(int i = 1; i < M - 5; i++)
for(int j = 1; j <= min(i, 5ll); j++) C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
cin >> q;
for(int i = 1, u, v, w; i <= q; i++)
{
cin >> u >> v >> w; res -= f[u][0];
for(int j = 0; j <= a[u]; j++)
{
double sum = 0;
for(int k = j; k <= min(j + w, now[u]); k++)
sum += f[u][k] * C[k][k - j] * C[now[u] - k][w - (k - j)];
f[u][j] = sum / C[now[u]][w];
}
res += f[u][0]; now[u] -= w; now[v] += w;
cout << res << '\n';
}
return 0;
}
参考文献: