「UR #19」通用测评号
「UR #19」通用测评号
首先将问题转化成:
有 \(n\) 个容量为无限大的油桶,每次等概率的选其中一个加入 \(1\) 单位的油量,问当所有油桶的油量都大于等于 \(b\) 时,油桶内油量大于等于 \(a\) 的概率。
正确性证明:
期望只与最终状态有关,如果加了 \(1\) 单位的油在大于等于 \(a\) 油量的桶里,不会对其他油桶状态产生影响,其他油桶状态改变的概率还是相等的。
或者用式子来表示,原问题的期望转移式为:
其中 \(i\) 是 \(sta\) 中油量大于等于 \(a\) 油量的桶的数量。
转化后问题的期望转移式为:
发现转移式是等价的。
由于每个油桶的贡献是 \(1\)。所以我们只要算出油桶油量大于等于 \(a\) 的概率即可。每个油桶等价,求出概率后乘以 \(n\) 就是答案了。
考虑油桶有贡献时的状态,当它刚好达到 \(a\) 时,至少有一个油桶的油量小于 \(b\)。
我们只要算出当它刚好到达 \(a\) 时,其他所有油桶油量大于等于 \(b\) 的概率,然后拿 \(1\) 减去即可。
发现当油桶油量大于等于 \(b\) 后再次添加不会改变状态,所以往其他油量小于 \(b\) 的油桶加油的概率相等。
设 \(f_{i,j}\) 表示此事已经有 \(i\) 个油桶的油量大于等于 \(b\)。总共加了 \(j\) 次有用的油(有用的油即加入到小于 \(b\) 的油桶的油和加入到钦定有贡献的那个油桶的油)
转移:
答案就是:
第一条转移方程可以理解为我们并没有指定这个油倒进哪个桶里,而是作为预备倒进桶里的
第二条转移方程就是从 预备倒进桶里的油选出 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;
}