今天整一道概率dp的题来做
题目链接(luogu)
codeforces
题目大意
给一个数 n n n,然后给出 n n n行,每行 l i l_i li, r i r_i ri,代表第 i i i个数在区间 [ l i , r i ] [l_i,r_i] [li,ri]中,求一个概率使得这 n n n个数中有 k k k%的数是 1 1 1开头的。
题目思路
啊哈,一看到这个概率,就想到了那段黑暗的时光
咳咳咳,回到正题
首先对于这个区间
[
l
,
r
]
[l, r]
[l,r]之间的数我们可以用差分的方式将它们开头是
1
1
1的概率求出来
令
d
p
[
i
]
dp[i]
dp[i]表示有
i
i
i个数的第
1
1
1位是
1
1
1的概率
然后呢再用一个概率
d
p
dp
dp,考虑每个区间的贡献,将其求出来
最后再暴力枚举
[
1
,
n
]
[1,n]
[1,n]累加满足条件的概率即可
代码
具体实现见代码
typedef long long ll;
ll count(ll n)
{
ll ans = 0, x = 1, cnt = 0, high = 0, num = n;
while (num)
{
high = num % 10;
num /= 10;
cnt++;
}
cnt--;
for (int i = 1; i <= cnt; i++, x *= 10)
ans += x;
if (high > 1)
ans += x;
else if (high == 1)
ans += n - x + 1;
return ans;
}
const int N = 1005;
double p[N], dp[N], ans;
int n, k;
ll l, r;
int main()
{
ios :: sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> l >> r;
ll tmp = count(r) - count(l - 1);
p[i] = 1.0 * tmp / (r - l + 1);
}
cin >> k;
dp[0] = 1;
for (int i = 1; i <= n; i++)
for (int j = n; j >= 0; j--)
{
dp[j] = dp[j] * (1 - p[i]);//当它不贡献时
if (j > 0)
dp[j] += dp[j - 1] * p[i];
}
for (int i = 0; i <= n; i++)
if (i * 100 >= n * k)
ans += dp[i];
printf ("%.15f", ans);
return 0;
}