• YYHS-分数(二分+容斥)


    题目描述

    KJDH是个十分善于探索的孩子,有一天他把分子分母小于等于n的最简分数列在了纸上,他想找到这些分数里第k小的数,这对于KJDH来说当然是非常轻易,但是KJDH最近多了很多妹子,他还要去找妹子聊天,所以这个任务就交给你了。

    输入

    输入文件只有一行,两个数n,k,保证输入合法。

    输出

    输出文件包含两个用空格隔开的数,x,y,表示第k小的分数x/y。

    样例输入

    5 6 100 200

    样例输出

    3 5 6 91

    提示

    n=5时,有这些分数1/2,1/3,2/3,1/4,3/4,1/5,2/5,3/5,4/5。其中第6小的是3/5。


    【数据规模与约定】

    对于60%的数据,n<=2000

    对于100%的数据,n<=50000
     

    题解

    这道题刚看到的时候完全没有头绪啊,打了暴力都感觉要T

    后来听了正解发现是个比较巧妙的思想

    我们首先二分一个小数

    然后再找到最接近这个小数的分数——比如说二分出来的mid,你要找最接近的分数,我们可以枚举2~n为分母,这样我们可以直接算出分子了

    找到分数后,我们需要计算小于等于这个分数的个数——那么我们同样也可以枚举2~n为分母,同样也可以算出分子,然后就是一个容斥啦

    具体可以看一下代码

     1 #include<bits/stdc++.h>
     2 #define N 50005
     3 using namespace std;
     4 int n,k,sum;
     5 bool flag[N];
     6 vector<int>prime[N];
     7 struct node{
     8     int x,y;
     9 };
    10 bool operator < (node x,node y){
    11     return x.x*y.y<x.y*y.x;
    12 }
    13 void dfs(int now,int s,int num,int limit,int x){
    14     if (now==prime[x].size()){
    15         if (num&1) sum-=limit/s;
    16                 else sum+=limit/s;
    17         return;
    18     }
    19     dfs(now+1,s,num,limit,x);
    20     dfs(now+1,s*prime[x][now],num+1,limit,x);
    21 }
    22 int calc(int x,int y){
    23     sum=0;
    24     dfs(0,1,0,x,y);
    25     return sum;
    26 }//计算<=x的数中与y的gcd值为1的个数 
    27 int check(int x,int y){
    28     int ans=0;
    29     for (int i=2;i<=n;i++){
    30         int s=i*x/y;
    31         ans+=calc(s,i);
    32     }
    33     return ans;
    34 }
    35 node find(double x){
    36     node s={0,1};
    37     for (int i=2;i<=n;i++){
    38         int y=(int)(x*i);
    39         if (s<(node){y,i}) s=(node){y,i};
    40     }
    41     return s;
    42 }
    43 void pre(){
    44     for (int i=2;i<=n;i++)
    45         if (!flag[i])
    46             for (int j=i;j<=n;j+=i)
    47                 flag[j]=true,prime[j].push_back(i);
    48 } 
    49 int main(){
    50     scanf("%d%d",&n,&k);
    51     pre();
    52     double l=0.0,r=1.0;//二分一个小数 
    53     while (true){
    54         double mid=(l+r)/2.0;
    55         node s=find(mid);//查找离这个小数最接近的分数 
    56         int num=check(s.x,s.y);//记录小于等于s的分数的个数 
    57         if (num<k) l=mid; else
    58         if (num>k) r=mid; else{
    59             printf("%d %d
    ",s.x,s.y);
    60             return 0;
    61         }
    62     }
    63     return 0;
    64 }
    View Code
  • 相关阅读:
    三元判断大小奇偶
    键盘输入 循环 for if-else while 等循环
    三元 导包
    进制
    符号等
    第二模块 4.1 模块的种类和介绍
    第二模块 练习题
    第二模块 3.15 迭代器
    第二模块 3.14 通过生成器实现并发编程
    第二模块 3.13 生成器
  • 原文地址:https://www.cnblogs.com/zhuchenrui/p/7778980.html
Copyright © 2020-2023  润新知