jzoj 100004. 【NOI2017模拟.4.1】 Dice

考场被题目秀到了。概率、期望。。。。。。

\(awsl\)

我们设:
\(g[i][j]\)表示投\(i\)次,最后为\(j\)的概率。
\(f[i][j]\)表示投\(i\)次,最后为\(j\)的期望。
\(s[i][j]\)表示投\(i\)次,最后为\(j\)的所有数和的平方的期望。

前面的转移方程显然:
\(g[i][j] = g[i-1][k]*p[j]/(1-p[k])\) (k=1...6&&k!=j)
\(f[i][j] = f[i-1][k]*p[j]/(1-p[k]) + g[i][j] * j\) (k=1...6&&k!=j)

而对于\(s\)的转移,我们可以回到定义。
我们设之前的数的和为\(k\)
\(s[i][j]\)的期望为\((k+j)^2\)的期望。
\(k^2+2*j*k+j^2\)
\(k^2\)可以由之前\(s\)转移。
\(2*j*k\)可以由\(f\)来转移。
\(j^2\)则是\(g\)来转移。
总的来说,就是:
\(s[i][j] = s[i-1][k]*p[j]/(1-p[k]) + 2 * (f[i][j]-g[i][j] * j) * j + g[i][j] * j * j\)(k=1...6&&k!=j)
然后转移即可。(PS:有精度问题)
精度问题可以看题解,题解的特有解决方法很妙。

code

#include <cstdio>
#define N 100010
#define db long double
#define mem(x, a) memset(x, a, sizeof x)
#define mpy(x, y) memcpy(x, y, sizeof y)
#define fo(x, a, b) for (int x = (a); x <= (b); x++)
#define fd(x, a, b) for (int x = (a); x >= (b); x--)
using namespace std;
int n, tot = 0;
db p[7], f[N][7], g[N][7], s[N][7], v[7];
db ans = 0, ans1 = 0;

int main()
{
	freopen("dice.in", "r", stdin);
	freopen("dice.out", "w", stdout);
	fo(i, 1, 6) scanf("%Lf", &p[i]);
	scanf("%d", &n);
	fo(i, 1, 6) g[1][i] = p[i], ans += g[1][i] * i;
	fo(i, 2, n)
		fo(j, 1, 6)
		{
			fo(k, 1, 6)
			{
				if (j == k) continue;
				g[i][j] += g[i - 1][k] * p[j] / (1 - p[k]);
			}
			ans += g[i][j] * j;
		}
	printf("%.15Lf\n", ans);
	fo(i, 1, 6) v[i] = i - ans / n;
	fo(i, 1, 6) s[1][i] = p[i] * v[i] * v[i], f[1][i] = p[i] * v[i];
	fo(i, 2, n)
		fo(j, 1, 6)
		{
			fo(k, 1, 6)
			{
				if (j == k) continue;
				f[i][j] += f[i - 1][k] * p[j] / (1 - p[k]);
				s[i][j] += s[i - 1][k] * p[j] / (1 - p[k]);
			}
			s[i][j] += 2 * f[i][j] * v[j] + g[i][j] * v[j] * v[j];
			f[i][j] += g[i][j] * v[j];
		}
	fo(i, 1, 6) ans1 += s[n][i];
	printf("%.15Lf\n", ans1);
	return 0;
}
posted @ 2020-01-13 16:20  jz929  阅读(98)  评论(0编辑  收藏  举报