• UVA11327 Enumerating Rational Numbers 题解


    Description

    Luogu传送门

    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\_ \]

  • 相关阅读:
    以&quot;小刀会“的成败论当今创业成败
    COCOS2D 学习笔记
    password加密的算法
    bzoj1087【SCOI2005】互不侵犯King
    HDU--2222--Keywords Search--AC自己主动机
    【leetcode】Subsets II (middle) ☆
    【leetcode】Word Search (middle)
    【hadoop2.6.0】利用JAVA API 实现数据上传
    【leetcode】Palindrome Partitioning II(hard) ☆
    【hadoop2.6.0】利用Hadoop的 Java API
  • 原文地址:https://www.cnblogs.com/xixike/p/15703998.html
Copyright © 2020-2023  润新知