第K小分数(二分)
Time limit:10000 ms
Memory limit:262144 kB
OS:Linux
Source:[Offer收割]编程练习赛46
judge:HihoCoder - 1692
描述
给定 N N N 个不同的质数 P 1 , P 2 , . . . , P N P_1, P_2, ... ,P_N P1,P2,...,PN 。用它们作为分目可以组成 ( P 1 − 1 ) + ( P 2 − 1 ) + . . . ( P N − 1 ) (P_1-1) + (P_2-1) + ... (P_N-1) (P1−1)+(P2−1)+...(PN−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},
P11,P12,P13,...,P1P1−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},
P21,P22,P23,...,P2P2−1,
.
.
.
,
...\ ,
... ,
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}
PN1,PN2,...,PNPN−1
请帮助小Ho求出其中第 K K K 小的分数。
Input
第一行包含两个整数 N N N 和 K K K 。
以下 N N N 行每行包含一个质数 P i P_i Pi 。
对于 70 % 70\% 70% 的数据, 1 ≤ N ≤ 100 , 1 ≤ K ≤ 1000000 , 2 ≤ P i ≤ 100000 1 ≤ N ≤ 100, 1 ≤ K ≤ 1000000, 2 ≤ P_i ≤ 100000 1≤N≤100,1≤K≤1000000,2≤Pi≤100000
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1000 , 1 ≤ K ≤ 1000000000 , 2 ≤ P i ≤ 1000000000 1 ≤ N ≤ 1000, 1 ≤ K ≤ 1000000000, 2 ≤ P_i ≤ 1000000000 1≤N≤1000,1≤K≤1000000000,2≤Pi≤1000000000
Output
输出一个分数表示答案
Sample Input
3 4
2
3
5
Sample Output
1/2
题解
因为给出的 p i p_i pi 是质数,所以每一个分数对应的小数都是独一无二的,就像只能有 1 2 \frac{1}{2} 21,而不会有 2 4 \frac{2}{4} 42 一样。
而且当分母一定时,分子的大小顺序就是对应的分数的大小顺序,就像 1 5 < 2 5 < 3 5 < 4 5 \frac{1}{5}<\frac{2}{5}<\frac{3}{5}<\frac{4}{5} 51<52<53<54
那么如果我们可以知道小于小数 m i d mid mid ,且分母为 P P P 的分数的个数 c n t cnt cnt 的话,我们就可以枚举 n n n 个分母求得所有小于 m i d mid mid 的分数的个数 c n t cnt cnt ,通过比较这个 c n t cnt cnt 和 k k k 的大小就可以进行二分了,直到最后 c n t = k cnt = k cnt=k 。
而每一次求 c n t cnt cnt 时我们都保存最接近 m i d mid mid 的分数的分子和分母,那么二分结束时的分子和分母就是要输出的答案了。
那么怎么获取这个 c n t cnt cnt 呢?我们知道当分母为 5 5 5 时,构成的分数是: 1 5 、 2 5 、 3 5 \frac{1}{5}、\frac{2}{5}、\frac{3}{5} 51、52、53和 4 5 \frac{4}{5} 54 ,假设 m i d mid mid 此时为 0.5 0.5 0.5 ,那么把 0.5 0.5 0.5 转化为一个分数且分母为 5 5 5 的最简单的方式就是分子分母同时乘以 5 5 5 : 0.5 = 5 ⋅ 0.5 5 0.5=\frac{5\ ·\ 0.5}{5} 0.5=55 ⋅ 0.5。所以得到的分数就是 2.5 5 \frac{2.5}{5} 52.5,显然,分子小于 2.5 2.5 2.5 的有 2 2 2 个,即 2.5 2.5 2.5 向下取整;同理当分母为 P P P 时,小于 m i d mid mid 的分数的个数就是 ⌊ P ⋅ m i d ⌋ \lfloor P\ ·\ mid \rfloor ⌊P ⋅ mid⌋,(符号意为向下取整)。
代码
#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;
}