[Tricks-00012]猜枚?概率密度!

原题是 qoj12999。

一个思路是算期望,发现期望一定是 \(0\),方差到是可以和 \(n\) 相关,不过误差太大了,没救!

不要反推了,考虑对每个 \(n=a\),计算 \(P(n=a|X1,X2,\dots,X_N)\)(肯定是取最大的,先验概率也很重要!)。

首先问题是:如何定义 \(P(X1,X2,\dots,X_N)\)?注意到 \(X_i\) 是连续的实数,所以概率应该都是 \(0\)。这里我们改为概率密度,也就是前缀概率的导数。

但是这东西显然算不出来,那么考虑一个特别强大的工具——贝叶斯公式\(P(A|B)=P(AB)/P(B)\)

转到这个题,相当于:

\[P(n=a|X1,X2,\dots,X_N)=\dfrac{P(n=a)P(X1,X2,\dots,X_N|n=a)}{P(X1,X2,\dots,X_N)} \]

题目保证先验概率,也就是 \(P(n=a)\),是均匀随机的,一定等于 \(\frac{1}{100}\)

于是,前面的条件概率,正比于 \(P(X1,X2,\dots,X_N|n=a)\)。又因为 \(X_i\) 是独立的,于是等于 \(\prod\limits_{i=1}^{N}P(X_i|n=a)\)

枚举每个 \(a\),将 \(P(X_i|n=a)\) 乘起来就好啦!

\(P(X_i|n=a)\) 怎么算呢?你可能会说,\(\lfloor X_i\sqrt{np(1-p)}+np+0.5 \rfloor\) 不就是 \(x_1+\dots+x_n\) 吗!如果这个结果是 \(k\),概率就应该是 \(C(n,k)p^k(1-p)^{n-k}\),做完了?

这样你就忽略了一个关键点——\(P(X_i|n=a)\) 算的是概率密度,和古典概型有一点点区别的。

最重要的就是必须保证 \(\int P(X|n=a)dX=1\)。在这道题上,\(np\) 的平移不重要,但一个长度为 \(1\) 的区间会除掉下面的分母,变成长度为 \(\frac{1}{\sqrt{np(1-p}}\) 的区间,因此 \(P\) 要乘上 \(\sqrt{np(1-p)}\)

最后把这一堆东西全乘起来比大小就行了。不过概率肯定超级小,所以取对数保证误差。提示:四舍五入写成 int a=round(x)+0.5 有点问题,为什么呢?

#include<bits/stdc++.h>
using namespace std;
const int m=100;
double he[m+5],l1[m+5],a[m+5],b[m+5];
int main(){
	int T;double p;
	scanf("%d%lf",&T,&p);
	double p1=log(p),p2=log(1-p);
	for(int i=1;i<=m;++i){
		l1[i]=l1[i-1]+log(i);
		a[i]=sqrt(i*p*(1-p)),b[i]=i*p;
	}
	while(T--){
		int N;scanf("%d",&N);
		for(int i=1;i<=m;++i)he[i]=N*log(sqrt(i*p*(1-p)));
		for(int i=1;i<=N;++i){
			double x;scanf("%lf",&x);
			for(int j=1;j<=m;++j){
				int k=round(x*a[j]+b[j])+10000.5;k-=10000;
				if(k<0||k>j)he[j]-=1e100;
				else he[j]+=l1[j]-l1[k]-l1[j-k]+k*p1+(j-k)*p2;
			}
		}
		int w=1;
		for(int i=2;i<=m;++i)if(he[w]<he[i])w=i;
		printf("%d\n",w);
	}
	return 0;
}
posted @ 2026-04-30 20:34  maihe  阅读(315)  评论(0)    收藏  举报