第K小分数(二分)

第K小分数(二分)

Time limit:10000 ms
Memory limit:262144 kB
OS:Linux
Source:[Offer收割]编程练习赛46
judge:HihoCoder - 1692

描述

给定 N N 个不同的质数 P 1 , P 2 , . . . , P N P_1, P_2, ... ,P_N 。用它们作为分目可以组成 ( P 1 1 ) + ( P 2 1 ) + . . . ( P N 1 ) (P_1-1) + (P_2-1) + ... (P_N-1) 个分数:

1 P 1 , 2 P 1 , 3 P 1 , . . . , P 1 1 P 1 , \frac{1}{P_1}, \frac{2}{P_1}, \frac{3}{P_1}, ..., \frac{P_1-1}{P_1},
1 P 2 , 2 P 2 , 3 P 2 , . . . , P 2 1 P 2 , \frac{1}{P_2}, \frac{2}{P_2}, \frac{3}{P_2}, ... ,\frac{P_2-1}{P_2},
. . .   , ...\ ,
1 P N , 2 P N , . . . , P N 1 P N \frac{1}{P_N}, \frac{2}{P_N}, ... ,\frac{P_N-1}{P_N}

请帮助小Ho求出其中第 K K 小的分数。

Input

第一行包含两个整数 N N K K

以下 N N 行每行包含一个质数 P i P_i

对于 70 % 70\% 的数据, 1 N 100 , 1 K 1000000 , 2 P i 100000 1 ≤ N ≤ 100, 1 ≤ K ≤ 1000000, 2 ≤ P_i ≤ 100000

对于 100 % 100\% 的数据, 1 N 1000 , 1 K 1000000000 , 2 P i 1000000000 1 ≤ N ≤ 1000, 1 ≤ K ≤ 1000000000, 2 ≤ P_i ≤ 1000000000

Output

输出一个分数表示答案

Sample Input

3 4
2
3
5

Sample Output

1/2

题解

因为给出的 p i p_i 是质数,所以每一个分数对应的小数都是独一无二的,就像只能有 1 2 \frac{1}{2} ,而不会有 2 4 \frac{2}{4} 一样。

而且当分母一定时,分子的大小顺序就是对应的分数的大小顺序,就像 1 5 < 2 5 < 3 5 < 4 5 \frac{1}{5}<\frac{2}{5}<\frac{3}{5}<\frac{4}{5}

那么如果我们可以知道小于小数 m i d mid ,且分母为 P P 的分数的个数 c n t cnt 的话,我们就可以枚举 n n 个分母求得所有小于 m i d mid 的分数的个数 c n t cnt ,通过比较这个 c n t cnt k k 的大小就可以进行二分了,直到最后 c n t = k cnt = k

而每一次求 c n t cnt 时我们都保存最接近 m i d mid 的分数的分子和分母,那么二分结束时的分子和分母就是要输出的答案了。

那么怎么获取这个 c n t cnt 呢?我们知道当分母为 5 5 时,构成的分数是: 1 5 2 5 3 5 \frac{1}{5}、\frac{2}{5}、\frac{3}{5} 4 5 \frac{4}{5} ,假设 m i d mid 此时为 0.5 0.5 ,那么把 0.5 0.5 转化为一个分数且分母为 5 5 的最简单的方式就是分子分母同时乘以 5 5 0.5 = 5     0.5 5 0.5=\frac{5\ ·\ 0.5}{5} 。所以得到的分数就是 2.5 5 \frac{2.5}{5} ,显然,分子小于 2.5 2.5 的有 2 2 个,即 2.5 2.5 向下取整;同理当分母为 P P 时,小于 m i d mid 的分数的个数就是 P     m i d \lfloor P\ ·\ mid \rfloor ,(符号意为向下取整)。

代码

#include <iostream>
#include <algorithm>
#define maxn 1005
#define _for(i, a) for(LL i = 0; i < (a); ++i)
using namespace std;
typedef long long LL;

LL n, k;
LL P[maxn];
LL Numerator, Denominator;	//最接近mid的分子和分母

LL find(double mid) {
	LL cnt = 0;
	_for(i, n) {
		LL num = mid * P[i];	//向下取整
		cnt += num;
		if (Numerator == -1 && Denominator == -1 || Numerator * P[i] < num * Denominator) {	//保存分子和分母
			Numerator = num;
			Denominator = P[i];
		}
	}
	return cnt;
}

LL gcd(LL a, LL b) {
	if (a > b) swap(a, b);
	return a ? gcd(b % a, a) : b;
}

void sol() {
	cin >> n >> k;
	_for(i, n) cin >> P[i];
	double l = 0, r = 1;
	while (1) {
		Numerator = -1, Denominator = -1;
		double mid = (l + r) / 2;
		LL cnt = find(mid);
		if (cnt == k) break;
		if (cnt > k) r = mid;
		else l = mid;
	}
	LL _gcd = gcd(Numerator, Denominator);
	cout << Numerator / _gcd << "/" << Denominator / _gcd << "\n";
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	//freopen("in.txt", "r", stdin);

	sol();
	return 0;
}
发布了163 篇原创文章 · 获赞 54 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/103074272