「loj - 2267」「SDOI2017」龙与地下城
description
简化版题意:抛出 \(Y\) 个点数在 \(0\dots X-1\) 的骰子,问点数总和在 \([A, B]\) 的概率。
solution
纯组合方法优化不下去。考虑题目输出的是浮点数,且精度要求比较低,我们采用连续分布拟合这个离散分布。
如果接触过概率论,不难想到正态分布(事实上中心极限定理表明正态分布是合适的拟合)。
那么求出期望 \(\mu = Y\times\frac{X-1}{2}\) 与方差 \(\sigma^2 = Y\times \frac{X^2-1}{12}\),则答案为:
\[\int_{A}^{B}\frac{e^{-\frac{(x-\mu)^2}{2\sigma^2}}}{\sqrt{2\pi}\sigma}dx
\]
此时你突然想起来正态分布的概率密度函数不定积分没有初等函数形式,直接 simpson 数值积分即可。
对于 \(XY\) 比较小的情况,直接做个背包即可。
code
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef double db;
const int LIMIT = 2048;
const db EPS = 1E-6;
const db COEF = sqrt(2 * acos(-1));
db pw2(db x) {return x * x;}
int X, Y, L; db e, v;
void init() {
e = 1.0 * (X - 1) / 2 * Y, v = sqrt(1.0 * (pw2(X) - 1) / 12 * Y), L = Y * (X - 1);
}
db f(db x) {
return exp(-pw2((x - e) / v) / 2) / (COEF * v);
}
db simpson(db L, db R) {
db M = (L + R) / 2;
return (f(L) + f(M)*4 + f(R)) / 6 * (R - L);
}
db sum(db L, db R) {
db M = (L + R) / 2, S = simpson(L, R), SL = simpson(L, M), SR = simpson(M, R);
return fabs(S - (SL + SR)) < EPS ? S : sum(L, M) + sum(M, R);
}
double dp[50000 + 5];
db get(int N) {
if( X*Y <= LIMIT ) {
for(int i=0;i<=L;i++) dp[i] = 0;
dp[0] = 1;
for(int i=1;i<=Y;i++) {
int t = i*(X - 1);
for(int j=t;j>=0;j--) {
dp[j] = dp[j] / X;
for(int k=1;k<X;k++)
if( j >= k ) dp[j] += dp[j - k] / X;
}
}
double ret = 0;
for(int i=0;i<=N;i++)
ret += dp[i];
return ret;
} else return sum(2*e-N, N) / 2;
}
void solve() {
scanf("%d%d", &X, &Y), init();
for(int i=1,A,B;i<=10;i++)
scanf("%d%d", &A, &B), printf("%.9f\n", get(B) - get(A - 1));
}
int main() {
int T; scanf("%d", &T);
while( T-- ) solve();
}
details
看了zzq的博客才发现C++竟然自带正态分布的函数erf。
题很好,但是我概率论连正态分布的式子都背不到只会抄。