「UR #19」通用测评号

「UR #19」通用测评号

首先将问题转化成:

\(n\) 个容量为无限大的油桶,每次等概率的选其中一个加入 \(1\) 单位的油量,问当所有油桶的油量都大于等于 \(b\) 时,油桶内油量大于等于 \(a\) 的概率。


正确性证明:

期望只与最终状态有关,如果加了 \(1\) 单位的油在大于等于 \(a\) 油量的桶里,不会对其他油桶状态产生影响,其他油桶状态改变的概率还是相等的。

或者用式子来表示,原问题的期望转移式为:

\[f_{sta}=\sum\dfrac{f_{nxt}}{n-i} \]

其中 \(i\)\(sta\) 中油量大于等于 \(a\) 油量的桶的数量。

转化后问题的期望转移式为:

\[\begin{aligned}f_{sta}&=\dfrac{i}{n}\sum\dfrac{f_{nxt}}{n} \\\dfrac{n-i}{n}f_{sta}&=\sum\dfrac{f_{nxt}}{n} \\f_{sta} &=\sum\dfrac{f_{nxt}}{n-i}\end{aligned} \]

发现转移式是等价的。


由于每个油桶的贡献是 \(1\)。所以我们只要算出油桶油量大于等于 \(a\) 的概率即可。每个油桶等价,求出概率后乘以 \(n\) 就是答案了。

考虑油桶有贡献时的状态,当它刚好达到 \(a\) 时,至少有一个油桶的油量小于 \(b\)

我们只要算出当它刚好到达 \(a\) 时,其他所有油桶油量大于等于 \(b\) 的概率,然后拿 \(1\) 减去即可。

发现当油桶油量大于等于 \(b\) 后再次添加不会改变状态,所以往其他油量小于 \(b\) 的油桶加油的概率相等。

\(f_{i,j}\) 表示此事已经有 \(i\) 个油桶的油量大于等于 \(b\)。总共加了 \(j\) 次有用的油(有用的油即加入到小于 \(b\) 的油桶的油和加入到钦定有贡献的那个油桶的油)

转移:

\[\begin{aligned}f_{i,j}&=f_{i,j}+\dfrac{f_{i,j-1}}{n-i} \\f_{i,j}&=f_{i-1,j-1}+\dfrac{f_{i-1,j-1}}{n-i+1}\times\binom{j-1-(i-1)\times b}{b-1}\end{aligned} \]

答案就是:

\[(1-(n-1)!\times f_{n-1,a-1+(n-1)\times b})\times n \]


第一条转移方程可以理解为我们并没有指定这个油倒进哪个桶里,而是作为预备倒进桶里的

第二条转移方程就是从 预备倒进桶里的油选出 b-1 个油倒进一个桶里

答案要乘 \((n-1)!\) 是因为我们是按顺序给桶塞油的,任意两个桶交换一下倒油的方案都行。

而记答案时是用 \(f_{n-1,a-1+(n-1)\times b}\) 而不是 \(f_{n-1,a+(n-1)\times b}\) 是因为我们必须保证最后一个油是倒进钦定有贡献的那个桶的,不然他可能在其他桶都到达 \(b\) 之前就到 \(a\) 了。

复杂度 \(O(n^2\times a)\)

#include<bits/stdc++.h>
#define Mod(x) ((x>=MOD)&&(x-=MOD))
#define ll long long
using namespace std;
const int MAXN = 305;
const int MOD = 998244353;
int n,a,b;
ll f[MAXN][MAXN*MAXN],fac[MAXN*MAXN],inv[MAXN*MAXN],I[MAXN];
ll qpw(ll x,ll b)
{
	ll r=1;
	for(;b;b>>=1,x=x*x%MOD) if(b&1) r=r*x%MOD;
	return r;
}
ll C(int n,int m)
{
	if(n<0||m<0) return 0;
	return fac[n]*inv[n-m]%MOD*inv[m]%MOD;
}
int main()
{
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
	scanf("%d %d %d",&n,&a,&b);
	fac[0]=1;for(int i=1;i<=(n-1)*b+a-1;++i) fac[i]=fac[i-1]*i%MOD;
	inv[(n-1)*b+a-1]=qpw(fac[(n-1)*b+a-1],MOD-2);
	for(int i=(n-1)*b+a-1-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%MOD;
	I[1]=1;for(int i=2;i<=n;++i) I[i]=I[MOD%i]*(MOD-MOD/i)%MOD;
	f[0][0]=1;
	for(int i=0;i<=n;++i)
	{
		for(int j=1;j<=(n-1)*b+a-1;++j)
		{
			f[i][j]+=f[i][j-1]*I[n-i]%MOD;
			Mod(f[i][j]);
			if(i) f[i][j]+=f[i-1][j-1]*I[n-i+1]%MOD*C(j-1-(i-1)*b,b-1)%MOD;
			Mod(f[i][j]);
		}		
	}
	ll Ans=0;
	Ans=(1-fac[n-1]*f[n-1][(n-1)*b+a-1]%MOD+MOD)%MOD*n%MOD;
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2022-04-28 19:55  夜空之星  阅读(50)  评论(0编辑  收藏  举报