F - Sugoroku2 解説 by lucifer1004


Let’s first simplify this problem to the following form:

  • There is a mission, our succeeding probability is \(p_s\), and the cost is \(E_s\), while our failing probability is \(p_f=1-p_s\), and the cost is \(E_f\).
  • We will stop as long as we succeed, otherwise we keep trying.
  • What is the expected value of our total cost?

The expected value of the total cost can be represented as:

\[ E=E_sp_s+(E_f+E_s)p_fp_s+(2E_f+E_s)p_f^2p_s+\cdots \]

It equals to:

\[ E_sp_s+p_fp_s((E_f+E_s)+(2E_f+E_s)p_f+\cdots) \]

We need to calculate two sums:

\[ \sum_s=E_s+E_sp_f+E_sp_f^2+\cdots=E_s\frac{1}{1-p_f}=\frac{E_s}{p_s} \]

and

\[ \sum_f=E_f+2E_fp_f+3E_fp_f^2+\cdots \]

which is a bit harder.

Actually, we have:

\[ \sum_fp_f=E_fp_f+2E_fp_f^2+\cdots \]

So we have:

\[ (1-p_f)\sum_f=E_f(1+p_f+p_f^2+\cdots)=E_f\frac{1}{1-p_f}=\frac{E_f}{p_s} \]

So

\[ \sum_f=\frac{E_f}{p_s^2} \]

Then we return to the original equation and now we have:

\[ E=E_sp_s+p_fp_s(\sum_f+\sum_s)=E_sp_s+p_fp_s(\frac{E_s}{p_s}+\frac{E_f}{p_s^2})=E_sp_s+(1-p_s)(E_s+\frac{E_f}{p_s}) \]

Now we return to the original problem and try to calculate \(E_s\), \(E_f\) and \(p_s\).

Here, if we arrive at the \(N\)-th position, we succeed. If we are sent back to the \(0\)-th position, we fail.

The idea is similar to LC837 - New 21 Game. We store the sum of the probability of the positions which can come to the current position, then

\[ p_i=\frac{\sum_p}{M} \]

Of course, the broken positions will not contribute to \(\sum_p\). Also, we need to add \(p_i\) (if \(i\) is not broken) and remove \(p_{i-M}\) (if \(i-M\) is not broken) when \(i\geq M\).

But here we also need the expected value \(E_i\), which can be represented as:

\[ E_i=\frac{\sum p_j(E_j+1)}{\sum_p}=1+\frac{\sum p_jE_j}{\sum_p} \]

The expression indicates that instead of calculating \(E_i\), we can calculate \(p_iE_i\) instead:

\[ p_iE_i=\frac{\sum_p}{M}+\frac{\sum p_jE_j}{M} \]

In a similar manner, we can calculate \(p_iE_i\) accumulatively. Note that the broken positions’ \(pE\) contributes to \(p_fE_f\) instead of \(\sum_{pE}\).

Since surpassing the \(N\)-th position is also counted as reaching the \(N\)-th position, our enumeration will stop at the \(N-1\)-th position. For each position that can reach the \(N\)-th position, we will calculate its contribution to \(p_N\) and \(p_NE_N\) according to the proportion \(\frac{i+M-N+1}{M}\).

Finally, we can get \(p_fE_f\), \(p_s=p_N\) and \(p_sE_s=p_NE_N\), and with these values, we can calculate the final answer \(E\).

  • Time complexity is \(\mathcal{O}(N)\).
  • Space complexity is \(\mathcal{O}(N)\).

Source code:

use proconio::input;

fn main() {
    input! {
        n: usize,
        m: usize,
        k: usize,
        a: [usize; k],
    }

    let mut broken = vec![false; n + 1];
    for i in a {
        broken[i] = true;
    }

    let mut prob_exp = vec![0.0; n + 1];
    let mut prob = vec![0.0; n + 1];
    prob[0] = 1.0;

    let mut prob_sum: f64 = 1.0;
    let mut exp_sum: f64 = 0.0;

    if m >= n {
        let goal_pct = (m - n + 1) as f64 / m as f64;
        prob[n] += prob[0] * goal_pct;
        prob_exp[n] += (prob_exp[0] + prob[0]) * goal_pct;
    }

    let mut prob_exp_fail = 0.0;

    for i in 1..n {
        prob[i] = prob_sum / m as f64;
        prob_exp[i] = prob[i] + exp_sum / m as f64;
        if !broken[i] {
            prob_sum += prob[i];
            exp_sum += prob_exp[i];
            if i + m >= n {
                let goal_pct = (i + m - n + 1) as f64 / m as f64;
                prob[n] += prob[i] * goal_pct;
                prob_exp[n] += (prob_exp[i] + prob[i]) * goal_pct;
            }
        } else {
            prob_exp_fail += prob_exp[i];
        }
        if i >= m && !broken[i - m] {
            prob_sum -= prob[i - m];
            exp_sum -= prob_exp[i - m];
        }
    }

    if prob[n] < 1e-8 {
        println!("-1");
    } else if (prob[n] - 1.0).abs() < 1e-8 {
        println!("{}", prob_exp[n]);
    } else {
        let exp_fail = prob_exp_fail / (1.0 - prob[n]);
        println!("{}", prob_exp[n] + (1.0 - prob[n]) * (prob_exp[n] + exp_fail) / prob[n]);
    }
}

投稿日時:
最終更新: