Description
Solution
又是一道诈骗题。
观察题目给出的伪代码,不难发现,对于每一个 \(d\),合法的 \(n\) 的个数有 \(\varphi(d)\) 个。
但是 \(k\) 这么大,我们怎么求呢?
继续观察样例,可以发现,样例中给出了 \(k\) 取最大值时的答案。
我们惊讶的发现,分母最大竟然只有 200000 !
所以我们就可以用线性筛筛出 \(2\times10^5\) 以内的 \(\varphi\),并做个前缀和。
对于每一组询问 \(k\),先二分出来小于等于 \(k\) 的最大的数是多少,用 \(k\) 减去它。然后对于当前分母暴力枚举分子,判断二者是否互质即可。
注意:\(\gcd(0, a) = a\),所以有且仅有 \(\gcd(0, 1) = 1\),要特判一下。
另外这题的边界也有一点恶心,建议自己好好思考一下。
Code
关于代码里的各种玄学写法,烦请各位自行模拟一下QwQ
#include <bits/stdc++.h>
#define ll long long
using namespace std;
namespace IO{
inline ll read(){
ll x = 0;
char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x;
}
template <typename T> inline void write(T x){
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const ll N = 2e5 + 10;
ll k, tmp, n, d;
ll phi[N], sum[N], p[N], tot;
bool vis[N];
inline void euler(){
phi[1] = 1;
for(ll i = 2; i < N; ++i){
if(!vis[i]) p[++tot] = i, phi[i] = i - 1;
for(ll j = 1; j <= tot && i * p[j] < N; ++j){
vis[i * p[j]] = 1;
if(i % p[j]) phi[i * p[j]] = phi[i] * (p[j] - 1);
else{
phi[i * p[j]] = phi[i] * p[j];
break;
}
}
}
phi[1]++;
for(ll i = 1; i < N; ++i) sum[i] = sum[i - 1] + phi[i];
}
inline ll gcd(ll a, ll b){
return !b ? a : gcd(b, a % b);
}
signed main(){
euler();
while(scanf("%lld", &k) && k){
if(k == 1) {puts("0/1"); continue;}
if(k == 2) {puts("1/1"); continue;}
d = upper_bound(sum, sum + N, k) - sum - 1;
k -= sum[d++];
if(!k) {printf("%lld/%lld\n", d - 2, d - 1); continue;}
for(n = 1; k; ++n) k -= (gcd(n, d) == 1);
printf("%lld/%lld\n", n - 1, d);
}
return 0;
}
\[\_EOF\_
\]