这是一个概率问题,需要计算在每个卡池都单抽一次的情况下,至少抽到一张想要的卡的概率。由于是概率计算,且需要输出模意义下的结果,我们需要用组合数学和模运算的知识来解决。
先想简单情况
举个例子:
- 池子1:3 种卡,你想要 1 种 → 抽一次,抽到你想要的概率是 1/3
- 池子2:4 种卡,你想要 1 种 → 概率 1/4
那至少一张抽到的概率 = 1 - 一张都没抽到的概率
一张都没抽到 = 池子1没抽到 * 池子2没抽到
= (2/3) * (3/4) = 6/12 = 1/2
所以至少一张抽到的概率 = 1 - 1/2 = 1/2
变成数学式子
设:
- 分子 mol = ∏(aᵢ - bᵢ) ← 这个是“全都没抽到”的分子
- 分母 den = ∏aᵢ ← 这个是“全都没抽到”的分母
全没抽到的概率 = mol / den
至少一张抽到的概率 = 1 - mol/den = (den - mol) / den
这就是我们要的分数:(den - mol) / den
模运算转换
题目要求输出这个分数在模 1e9+7 下的值。
比如 1/2 在模运算下是 500000004,因为:
2 * 500000004 除以 1e9+7 余数是 1。
怎么转换分数到模数下?
(den - mol) / den在模运算下 = (den - mol) * den⁻¹ mod MOD
这里 den⁻¹是 den 的“逆元”,就是在模 MOD 下乘上 den 得到 1 的那个数。
逆元计算公式:den⁻¹ = den^(MOD-2) mod MOD(费马小定理,MOD 是质数)
def inv(num):
return pow(num,MOD-2,MOD)
也就是以上代码(c++版需要手动实现快速幂)
总结
1 减去(每个池子都没抽到想要的概率)
然后把这个分数用模运算的形式输出。
代码就是把上面公式翻译成乘法 + 取模 + 用快速幂求一次逆元。
示例代码
import sys
input=sys.stdin.readline
n=int(input())
MOD=10**9+7
a=list(map(int,input().split()))
b=list(map(int,input().split()))
mol=1
den=1
def inv(num):
return pow(num,MOD-2,MOD)
for i in range(n):
mol*=a[i]-b[i]
den*=a[i]
mol%=MOD
den%=MOD
print((den-mol)*inv(den)%MOD)
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 1e9 + 7;
long long mod_pow(long long a, long long b, long long mod) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
long long inv(long long num) {
return mod_pow(num, MOD - 2, MOD);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
vector<long long> a(n), b(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
for (int i = 0; i < n; i++) {
cin >> b[i];
}
long long mol = 1, den = 1;
for (int i = 0; i < n; i++) {
long long diff = (a[i] - b[i]) % MOD;
if (diff < 0) diff += MOD;
mol = mol * diff % MOD;
den = den * (a[i] % MOD) % MOD;
}
long long result = (den - mol) % MOD;
if (result < 0) result += MOD;
result = result * inv(den) % MOD;
cout << result << endl;
return 0;
}

京公网安备 11010502036488号