前言
前几场的GYM遇见了一个概率递推题,发现概率题这一类的出的比较多,这里做一个总结。
牛清楚的裙子!!!
题目链接:牛清楚的裙子!!!
题目大意:有n条裙子。牛清楚穿一号裙获得10000点欢乐值,其他裙子获得1点欢乐值,每次随机穿一条,求问欢乐值的期望。
数据范围:
t
≤
1
e
5
,
n
≤
1
e
7
t\le 1e5,n\le 1e7
t≤1e5,n≤1e7
题解:非常经典的概率题了,一般这类随机穿的题目,我们都可以采取
d
p
dp
dp来解决。考虑每次要穿的期望次数。令
f
[
i
]
f[i]
f[i]表示已经穿过i条不同裙子,然后要结束的期望局数,显然有
f
[
n
]
=
0
f[n]=0
f[n]=0,而我们要求的就是
f
[
0
]
f[0]
f[0]。考虑递推式:
f
[
i
]
=
i
n
∗
(
f
[
i
]
+
1
)
+
n
−
i
n
∗
(
f
[
i
+
1
]
+
1
)
f[i]=\frac{i}{n}*(f[i]+1)+\frac{n-i}{n}*(f[i+1]+1)
f[i]=ni∗(f[i]+1)+nn−i∗(f[i+1]+1)解释下就是有
i
i
i条穿过的,
n
−
i
n-i
n−i条未穿过的,然后穿一次的转移。我们简化下式子有
f
[
i
]
=
f
[
i
+
1
]
+
n
n
−
i
f[i]=f[i+1]+\frac{n}{n-i}
f[i]=f[i+1]+n−in然后我们知道
f
[
n
]
=
0
f[n]=0
f[n]=0。所以
f
[
n
−
1
]
=
0
+
n
1
f[n-1]=0+\frac{n}{1}
f[n−1]=0+1n,
f
[
n
−
2
]
=
0
+
n
1
+
n
2
f[n-2]=0+\frac{n}{1}+\frac{n}{2}
f[n−2]=0+1n+2n以此类推
f
[
0
]
=
∑
i
=
1
n
n
i
=
n
∗
∑
i
=
1
n
1
i
f[0]=\sum\limits_{i=1}^{n}{\frac{n}{i}}=n*\sum\limits_{i=1}^{n}{\frac{1}{i}}
f[0]=i=1∑nin=n∗i=1∑ni1.即有
n
n
n条裙子时的期望局数为
f
[
0
]
f[0]
f[0],所以每一条裙子期望穿的次数为
f
[
0
]
n
=
∑
i
=
1
n
1
i
\frac{f[0]}{n}=\sum\limits_{i=1}^{n}{\frac{1}{i}}
nf[0]=i=1∑ni1,所以
a
n
s
=
(
n
+
9999
)
∗
∑
i
=
1
n
1
i
ans=(n+9999)*\sum\limits_{i=1}^{n}{\frac{1}{i}}
ans=(n+9999)∗i=1∑ni1
用
d
p
dp
dp数组
O
(
n
)
O(n)
O(n)预处理下
∑
i
=
1
n
1
i
\sum\limits_{i=1}^{n}{\frac{1}{i}}
i=1∑ni1,就可以快速求出答案了。
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
const ll N = 10000000 + 10;
const int mod = 1e9 + 7;
int t, n;
double dp[N];
int main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
for (int i = 1; i <= 1e7; i++)
{
dp[i] = dp[i - 1] + 1.0 / i;
}
read(t);
while (t--)
{
read(n);
double ans = (n + 9999)* dp[n];
printf("%.7lf\n", ans);
}
return 0;
}
A. Sticker Album
题目链接:Sticker Album
题目大意:要集满
n
n
n个邮票,一个包裹里面有
a
−
b
a-b
a−b个邮票(均匀的整数分布)。求集满
n
n
n个邮票期望要的包裹数量。
数据范围:
1
≤
n
≤
1
e
6
,
0
≤
a
,
b
≤
1
e
6
1\le n\le 1e6,0\le a,b\le 1e6
1≤n≤1e6,0≤a,b≤1e6
题解:也是应该概率递推题。我们设
d
p
[
i
]
dp[i]
dp[i]为手中已经有了
i
i
i个邮票要集满的期望包裹数。
d
p
[
n
]
=
0
,
d
p
[
0
]
dp[n]=0,dp[0]
dp[n]=0,dp[0]即是答案。我们考虑如何转移。
d
p
[
i
]
=
1
b
−
a
+
1
∑
j
=
a
b
(
d
p
[
i
+
j
]
+
1
)
=
1
+
1
b
−
a
+
1
∑
j
=
a
b
(
d
p
[
i
+
j
]
)
dp[i]=\frac{1}{b-a+1}\sum\limits_{j=a}^{b}(dp[i+j]+1)=1+\frac{1}{b-a+1}\sum\limits_{j=a}^{b}(dp[i+j])
dp[i]=b−a+11j=a∑b(dp[i+j]+1)=1+b−a+11j=a∑b(dp[i+j])。记录一下前缀和然后转移即可?我们需要去注意
a
=
0
a=0
a=0的情况。当
a
=
0
a=0
a=0时,转移方程是
d
p
[
i
]
=
1
+
1
b
+
1
∑
j
=
0
b
(
d
p
[
i
+
j
]
)
=
1
+
1
b
+
1
∑
j
=
1
b
(
d
p
[
i
+
j
]
)
+
1
b
+
1
d
p
[
i
]
dp[i]=1+\frac{1}{b+1}\sum\limits_{j=0}^{b}(dp[i+j])=1+\frac{1}{b+1}\sum\limits_{j=1}^{b}(dp[i+j])+\frac{1}{b+1}dp[i]
dp[i]=1+b+11j=0∑b(dp[i+j])=1+b+11j=1∑b(dp[i+j])+b+11dp[i],这里的转移两边都有
d
p
[
i
]
dp[i]
dp[i],我们需要特判下,将等式右边的
d
p
[
i
]
dp[i]
dp[i]挪到左边。剩下的就是记录前缀和然后对应转移了。
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
const ll N = 2000000 + 10;
const int mod = 1e9 + 7;
int n,a,b;
double dp[N],sum[N];
int main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
read(n), read(a), read(b);
int len = b - a + 1;
dp[n] = 0.0;
for (int i = n - 1; i >= 0; i--)
{
if (!a)
{
dp[i] = (sum[i + 1] - sum[i + b + 1]+len) / (len * 1.0);
dp[i] = dp[i] * (1.0*len) / (1.0 * len - 1);
}
else
{
dp[i] = (sum[i + a] - sum[i + b + 1]+len) / (len * 1.0);
}
sum[i] = sum[i + 1] + dp[i];
}
printf("%lf\n", dp[0]);
return 0;
}
P4550 收集邮票
题目链接:收集邮票
题目大意:有n种不同的邮票,皮皮想收集所有种类的邮票。唯一的收集方法是到同学凡凡那里购买,每次只能买一张,并且买到的邮票究竟是n种邮票中的哪一种是等概率的,概率均为1/n。但是由于凡凡也很喜欢邮票,所以皮皮购买第k张邮票需要支付k元钱。
现在皮皮手中没有邮票,皮皮想知道自己得到所有种类的邮票需要花费的钱数目的期望。
数据范围:
1
≤
n
≤
1
e
4
1\le n\le 1e4
1≤n≤1e4
题解:好题!求出期望要买
x
x
x张邮票,那么要花的钱期望为
(
x
+
1
)
x
2
=
x
2
+
x
2
\frac{(x+1)x}{2}=\frac{x^2+x}{2}
2(x+1)x=2x2+x。套路的设计
d
p
[
i
]
dp[i]
dp[i]表示已经已经集齐
i
i
i种邮票,集满期望的次数。然后将
d
p
[
0
]
dp[0]
dp[0]带入
x
x
x,完美结束。。。。。等等,好像样例都不过去。。。。问题在哪?我们重新看
x
2
+
x
2
\frac{x^2+x}{2}
2x2+x这个式子,其中的
x
2
x^2
x2表示的是局数平方的期望,而我们直接将
x
x
x带入求出的
x
2
x^2
x2表示的是局数期望的平方。所以我们不能直接带入,还需要求出一个
d
p
2
[
i
]
dp2[i]
dp2[i]表示已经已经集齐
i
i
i种邮票,集满次数平方的期望。然后将
d
p
2
[
0
]
dp2[0]
dp2[0]和
d
p
[
0
]
dp[0]
dp[0]再带入就行了。
AC代码:
#include<bits/stdc++.h>
#define ld long double
#define ll long long
using namespace std;
template<class T>
void read(T& x)
{
T res = 0, f = 1; char c = getchar();
while (!isdigit(c)) {
if (c == '-')f = -1; c = getchar();
}
while (isdigit(c)) {
res = (res << 3) + (res << 1) + c - '0'; c = getchar();
}
x = res * f;
}
const ll N = 200000 + 10;
const int mod = 1e9 + 7;
int n;
double ans,f[N],f2[N];
int main()
{
//ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
#endif // ONLINE_JUDGE
read(n);
double ans=0;
for (int i = n - 1; i >= 0; i--)
{
f[i] = f[i + 1] + 1.0*n / (n - i);
f2[i] = 2.0 * i / (1.0 * n - i) * f[i] + f2[i + 1] + 2.0 * f[i + 1] + 1.0*n / (1.0 * n - i);
}
printf("%.2lf\n", (f[0] + f2[0]) / 2);
return 0;
}