ICPC Greater New York Region 2020 E Marbles

6 篇文章 0 订阅
2 篇文章 0 订阅

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)nm,也就是 选 出 一 红 一 绿 的 方 案 数 选 出 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值