题目要求和m互质的第k大。
那么二分区间,统计区间内和m互质的数个数,所有个数为k结果最小的就是答案。
k比较大最初二分的右端点开大点。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define maxn 1111111
#define count Count
long long m, k;
bool is_prime[maxn];
long long prime[maxn], cnt;
long long fenjie[55], pr_cnt;
void get_prime () {
memset (is_prime, 1, sizeof is_prime);
is_prime[0] = is_prime[1] = 0;
cnt = 0;
for (int i = 2; i < maxn; i++) {
if (is_prime[i]) {
prime[++cnt] = i;
for (int j = i+i; j < maxn; j += i)
is_prime[j] = 0;
}
}
}
void work (long long num) {
pr_cnt = 0;
for (int i = 1; i <= cnt && prime[i] <= num/prime[i]; i++) {
if (num%prime[i] == 0) {
fenjie[++pr_cnt] = prime[i];
while (num%prime[i] == 0)
num /= prime[i];
}
}
if (num > 1)
fenjie[++pr_cnt] = num;
}
int count (int num, long long &mul) {
mul = 1;
int ans = 0;
for (int i = 1; i <= pr_cnt; i++, num >>= 1) {
if (num&1) {
ans++;
mul *= fenjie[i];
}
}
return ans;
}
long long solve (long long Max) { //[1,Max]中和m互质的数
long long ans = 0;
for (long long i = 1; i < (1<<pr_cnt); i++) {
long long mul;
int num_of_1 = count (i, mul);
if (num_of_1&1) {
ans += Max/mul;
}
else ans -= Max/mul;
}
return Max-ans;
}
int main () {
get_prime ();
while (scanf ("%lld%lld", &m, &k) == 2) {
work (m);
long long l = 1, r = 1e9;
long long mid;
while (r-l > 1) {
mid = (l+r)>>1;
long long cur = solve (mid);
if (cur >= k)
r = mid;
else l = mid;
}
if (solve (l) == k) printf ("%lld\n", l);
else printf ("%lld\n", r);
}
return 0;
}