E Marbles
题目描述
For a booth at the campus spring carnival, Toni wants to have players draw two marbles from a bowl of red and green marbles. Players go on to the next level of the game if one marble is red and one is green.
Toni wants to be able to choose the exact probability of drawing one red and one green marble. She wants enough marbles in the bowl to make it hard to guess the probability, but she needs to be able to limit the maximum number of marbles since her bowl is only so big.
Write a program to find out how many red and how many green marbles to put in the bowl.
输入描述:
Input consists of a single line containing four space separated decimal integers: p, q, N and M. Youare to find the number of red marbles ® and green (g) marbles so that r ≤ g, N ≤ (r+g) ≤ M <= 1000, 2 ≤ N ≤ 1000 and (r+g) is the smallest sum ≥ N. In addition, the probability of drawing one red marbleand one green marble (not necessarily in that order) when drawing exactly two marbles at random from the bowl is exactly p/q. q > 0, GCD(p, q) will always be 1. If no solution exists that meet Toni’s requirements, then print out NO SOLUTION.
输出描述:
The single output line consists of two space separated decimal integers r followed by g or the words NO SOLUTION if no solution exists for the supplied input.
示例1
输入
1 1 2 10
输出
1 1
示例2
输入
2 3 4 10
输出
2 2
示例3
输入
2 5 10 15
输出
NO SOLUTION
题目大意:
一堆弹珠是由红色弹珠和绿色弹珠组成的,问红色和绿色的个数各是多少时,可以让从里面同时选出两个弹珠恰好一红一绿的概率为p/q。
题目给定p, q, N and M,要求红色和绿色弹珠的数量满足r ≤ g, N ≤ (r+g) ≤ M <= 1000, 2 ≤ N ≤ 1000。
题解:
从n个红弹珠m个绿色弹珠里面恰好选出一红一绿的概率为 n ∗ m C ( 2 , n + m ) \frac{n*m}{C(2, n+m)} C(2,n+m)n∗m,也就是 选 出 一 红 一 绿 的 方 案 数 选 出 2 个 弹 珠 的 方 案 数 \frac{选出一红一绿的方案数}{选出2个弹珠的方案数} 选出2个弹珠的方案数选出一红一绿的方案数。
因为题目给的数据量不大,所以我们可以在范围内枚举红球和绿球的个数,用公式算出概率,将概率为p/q的方案输出即可,如果没有满足要求的方案输出“NO SOLUTION”。
虽然枚举红球绿球的个数不会超时,但是如果用n!的方法来计算组合数C(2, n+m)的话依然会超时,所以这题的关键就在于如何快速计算出组合数。
求组合数可以用费马小定理来进行优化,具体可以看Acwing上的题解:
AcWing 886. 费马小定理, 快速冥, 求组合数 II
经过预处理我们可以将计算组合数的时间复杂度降到O(1),优化时间复杂度之后就可以AC了。
AC代码:
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int a, int k, int p) { // 快速幂
int res = 1;
while (k) {
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ ) { // 预处理,后面可以O(1)计算组合数
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
double p, q, n, M;
cin >> p >> q >> n >> M;
for (double r = 0; r <= M; r ++) { // 在范围内枚举红色弹珠和绿色弹珠的个数
for (double g = r; g <= M - r; g ++) {
if ((r + g) < n) continue; // 个数超出范围
int a = r + g, b = 2;
double t2 = (LL)fact[a] * infact[b] % mod * infact[a - b] % mod; // 计算组合数C(2, r+g)
double t = (r * g) / t2; // 计算恰好选出一红一绿的概率
if (t == p / q) { // 如果概率为p / q就输出红绿弹珠个数
cout << r << ' ' << g << '\n';
return 0;
}
}
}
cout << "NO SOLUTION\n"; // 没有方案满足
return 0;
}