题解 基础概率练习题

传送门

主要有两种做法

考虑第一个子任务
因为每个位置都是完全等价的所以答案显然是 \(\frac{1}{n}\)
考虑所有 \(\geqslant k\) 的位置,发现如果钦定一些位置 \(\geqslant k\) 的话这些位置也是等价的
所以问题其实可以被转化成钦定 \(\geqslant k\) 的位置,然后计算其它位置都 \(<k\) 的方案数
若有 \(c\) 个位置 \(\geqslant k\) 的话,有

\[\sum\limits_{c=1}^{n-1}\binom{n-1}{n-c-1}\frac{1}{c}\sum\limits_{i=0}^c(-1)^i\binom{c}{i}\binom{m-(c+i)k+n-1}{n-1} \]

发现 \(c+i\) 相等时后面那个东西都是一样的,所以枚举 \(c+i\)
然后大力变形发现是一个 \(O(n)\) 的式子,于是就可以写了

  • \(n\) 很大且只需要求 \(\forall i\in[1, n], \frac{1}{i!}\pmod p\) 时,逆推比正推快太多了

然后题解做法感觉很神奇,所以粘一下题解做法:


加强自 CF1096E。

本来这题是 T2,后来因为 T1 可能比较难写且 T2 验题人给了一个很清新的做法,这题就放到了 T1。下面就说验题人的做法。

考虑条件概率 \(P(A \mid B)=\frac{P(AB)}{P(B)}\) 表示在 B 发生的情况下 A 发生的概率,就等于 AB 同时发生的概率除以 B 发生的概率。这题中 A 是第一个人 rk1,B 是第一个人得分 \(\ge k\)

那么 \(p_1 = P(B)\) 就是拿第一个人得分 \(\ge k\) 的方案数去除以没有任何限制的方案数。

\(p_2 = P(AB)\) 就是第一个人 rk1 且得分 \(\ge k\) 的概率。我们发现对于每个人,这个概率是相同的,并且只要有人得分 \(\ge k\) 那么就一定会对这个概率产生贡献(rk1 得分一定 \(\ge k\))。

那么 \(n \times p_2\) 就是拿至少一个人的得分 \(\ge k\) 的情况除以没有任何限制的情况,然后就能直接解出 \(p_2\)

\(\frac{p_2}{p_1}\) 就是答案。时间复杂度 \(O(n + m)\)


点击查看代码
#pragma GCC optimize(3)
// #pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 20000010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m, k;
int lim;
const ll mod=998244353;
ll fac[N], inv[N], inv2[N];
inline ll C(int n, int k) {return n<k?0:fac[n]*inv2[k]%mod*inv2[n-k]%mod;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}

namespace task1{
	ll ans, sum;
	void solve() {
		if (n==1) {puts("1"); return ;}
		for (int t=k; t<=m; ++t) {
			for (int c=1; c<=n&&t*c<=m; ++c) {
				ll tem=0;
				for (int i=0; i<=n&&(i+c)*t<=m; ++i)
					tem=(tem+(i&1?-1:1)*C(n-c, i)*C(m-c*t-i*t+n-c-1, n-c-1))%mod;
				// cout<<"t&c: "<<t<<' '<<c<<' '<<tem<<endl;
				ans=(ans+C(n-1, c-1)*tem%mod*inv[c])%mod;
			}
		}
		for (int i=k; i<=m; ++i) sum=(sum+C(m-i+n-2, n-2))%mod;
		ans=ans*qpow(sum, mod-2)%mod;
		printf("%lld\n", (ans%mod+mod)%mod);
	}
}

namespace task{
	ll ans, sum;
	void solve() {
		for (int t=1; t<=n&&t*k<=m; ++t)
			ans=(ans+((t+1)&1?-1:1)*fac[m-t*k+n-1]*inv2[m-t*k]%mod*inv2[n-t]%mod*inv2[t])%mod;
		// for (int c=1; c<=n; ++c) {
		// 	ll tem=0;
		// 	for (int i=0; i<=n-c; ++i)
		// 		tem=(tem+(i&1?-1:1)*C(n-c, i)*C(m-c*k-i*k+n-1, n-1))%mod;
		// 	ans=(ans+C(n-1, c-1)*inv[c]%mod*tem)%mod;
		// }
		for (int i=k; i<=m; ++i) sum=(sum+C(m-i+n-2, n-2))%mod;
		ans=ans*qpow(sum, mod-2)%mod;
		printf("%lld\n", (ans%mod+mod)%mod);
	}
}

signed main()
{
	freopen("probability.in", "r", stdin);
	freopen("probability.out", "w", stdout);

	n=read(); m=read(); k=read(); lim=max(n, m)<<1;
	if (!k) {printf("%lld\n", qpow(n, mod-2)); return 0;}
	fac[0]=fac[1]=1; inv[0]=inv[1]=1; inv2[0]=inv2[1]=1;
	for (int i=2; i<=lim; ++i) fac[i]=fac[i-1]*i%mod;
	inv2[lim]=qpow(fac[lim], mod-2);
	for (int i=lim; i; --i) inv2[i-1]=inv2[i]*i%mod;
	// for (int i=2; i<=lim; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	// for (int i=2; i<=lim; ++i) inv2[i]=inv2[i-1]*inv[i]%mod;
	// task1::solve();
	task::solve();

	return 0;
}
posted @ 2022-06-29 06:58  Administrator-09  阅读(3)  评论(0编辑  收藏  举报