题目链接

抢红包

题目描述

一个红包总金额为 元,有 个人来抢。规则如下:如果红包里还剩 元,下一个人抢到的金额是从区间 中等概率均匀随机出的一个实数。

求第 个人期望抢到多少钱,结果对 取模。

输入:

  • 输入一行,包含三个整数

输出:

  • 输出一行,表示第 个人期望抢到的钱数对 取模后的结果。

解题思路

这是一个经典的期望问题,可以通过线性递推来解决。

1. 定义状态

  • 为第 个人抢到的金额(一个随机变量)。
  • 为第 个人抢红包之前,红包里剩余的金额(也是一个随机变量)。
  • 我们要求的是第 个人的期望金额,即

2. 分析单次期望

  • 根据题目规则,如果红包里有 元,抢到的金额是 上的均匀分布。一个在 上均匀分布的随机变量,其期望值为
  • 因此,在已知红包剩余 的情况下,第 个人抢到金额的条件期望是

3. 建立递推关系

  • 根据全期望公式,
  • 这说明,第 个人的期望金额是抢红包前剩余金额的期望值的一半。
  • 接下来,我们分析剩余金额期望 的关系。第 个人抢之前剩余的钱 ,等于第 个人抢之前剩余的钱 减去第 个人抢走的钱 。即
  • 两边取期望,利用期望的线性性质,得到
  • 代入,得到

4. 求解通项公式

  • 我们得到了一个关于剩余金额期望的递推式:
  • 初始条件是 (第一个人抢之前,红包里有全部金额)。
  • 这是一个等比数列,可以得到通项公式:
  • 因此,第 个人抢到金额的期望为

5. 计算结果

  • 我们需要计算
  • 这等价于计算
  • 模数 是一个质数,我们可以使用费马小定理来求乘法逆元。对于质数模
  • 一个高效的计算方法是先求出 的逆元 inv2,然后计算 inv2 次方。
  • inv2
  • 最终结果是
  • 所有幂运算都可以通过快速幂来高效完成。

代码

#include <iostream>

using namespace std;

typedef long long LL;

LL power(LL base, LL exp) {
    LL res = 1;
    LL mod = 1e9 + 7;
    base %= mod;
    while (exp > 0) {
        if (exp % 2 == 1) res = res * base % mod;
        base = base * base % mod;
        exp /= 2;
    }
    return res;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    LL w, n, k;
    cin >> w >> n >> k;

    LL mod = 1e9 + 7;
    
    // 计算 2 的乘法逆元
    LL inv2 = power(2, mod - 2);
    
    // 计算 2^k 的乘法逆元,即 (2的逆元)^k
    LL inv2_k = power(inv2, k);
    
    // 期望 = W * (1/2^k)
    LL ans = (w * inv2_k) % mod;
    
    cout << ans << endl;

    return 0;
}
import java.util.Scanner;

public class Main {
    static long power(long base, long exp) {
        long res = 1;
        long mod = 1_000_000_007;
        base %= mod;
        while (exp > 0) {
            if (exp % 2 == 1) res = (res * base) % mod;
            base = (base * base) % mod;
            exp /= 2;
        }
        return res;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long w = sc.nextLong();
        long n = sc.nextLong();
        long k = sc.nextLong();

        long mod = 1_000_000_007;

        // 计算 2 的乘法逆元
        long inv2 = power(2, mod - 2);

        // 计算 2^k 的乘法逆元,即 (2的逆元)^k
        long inv2_k = power(inv2, k);

        // 期望 = W * (1/2^k)
        long ans = (w * inv2_k) % mod;

        System.out.println(ans);
    }
}
import sys

def power(base, exp):
    res = 1
    mod = 10**9 + 7
    base %= mod
    while exp > 0:
        if exp % 2 == 1:
            res = (res * base) % mod
        base = (base * base) % mod
        exp //= 2
    return res

def main():
    w, n, k = map(int, sys.stdin.readline().split())
    mod = 10**9 + 7
    
    # 计算 2 的乘法逆元
    inv2 = power(2, mod - 2)
    
    # 计算 (1/2)^k
    inv2_k = power(inv2, k)
    
    # 期望 = W * (1/2^k)
    ans = (w * inv2_k) % mod
    
    print(ans)

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:数学期望 + 快速幂求逆元
  • 时间复杂度:算法主要由两次快速幂运算构成。一次用于求 的逆元,其指数为 ;另一次用于求逆元的 次方。总时间复杂度为
  • 空间复杂度:只需要常数个变量来存储中间结果,所以空间复杂度为