CF54C First Digit Law Solution

今天整一道概率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;
}

谢谢

你可能感兴趣的:(c++)