[2019HDU多校第一场][HDU 6584][G. Meteor]

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6584

题目大意:求所有满足\(0<\frac{p}{q}\leq1, gcd(p,q)=1,p\leq n,q\leq n\)的分数中,第\(k\)小的分数

题解:考虑二分答案,并用分数形式记录。假设当前二分的分数为\(\frac{p}{q}\),则小于等于这个分数的个数为

$$\sum_{i=1}^{n}\sum_{j=1}^{\left \lfloor \frac{pi}{q} \right \rfloor}[gcd(i,j)==1]=\sum_{i=1}^{n}\sum_{j=1}^{\left \lfloor \frac{pi}{q} \right \rfloor}\sum_{d|i,d|j}^{ }\mu(d)=\sum_{i=1}^{n}\sum_{d|i}^{ }\mu(d)\left \lfloor \frac{pi}{qd} \right \rfloor=\sum_{d=1}^{n}\mu(d)\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\left \lfloor \frac{pi}{q} \right \rfloor$$

   这个式子可以通过预处理莫比乌斯函数的值,然后分块加类欧做到\(O(\sqrt{n}logn)\)的时间复杂度求解

   在二分出答案后,只需找到最小的大于等于\(\frac{p}{q}\)的分数即可。官方题解使用了\(Stern-Brocot Tree\),实际上通过枚举分母也可以做到\(O(n)\)求解

 

我的代码跑了7347ms...和之前的A还有K题一样极限_(:з」∠)_

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define N 1000001
  4 #define LL long long
  5 LL T,n,k,cnt,p[N],mu[N],v[N];
  6 #undef LL
  7 struct Frac
  8 {
  9     #define LL __int128
 10     LL p,q;
 11     void simp()
 12       {
 13       LL d=__gcd(p,q);
 14       p/=d,q/=d;
 15       }
 16     Frac operator +(const Frac &t)const
 17       {
 18       Frac tmp;
 19       tmp.q=q*t.q;
 20       tmp.p=p*t.q+q*t.p;
 21       tmp.simp();
 22       return tmp;
 23       }
 24     Frac operator /(const LL &t)const
 25       {
 26       Frac tmp;
 27       tmp.p=p;
 28       tmp.q=q*t;
 29       tmp.simp();
 30       return tmp;
 31       }
 32     bool operator <(const Frac &t)const
 33       {
 34       return p*t.q<q*t.p;
 35       }
 36     bool operator <=(const Frac &t)const
 37       {
 38       return p*t.q<=q*t.p;
 39       }
 40     #undef LL
 41 };
 42 void pretype()
 43 {
 44     #define LL long long
 45     mu[1]=1;
 46     for(LL i=2;i<N;i++)
 47       {
 48       if(!v[i])p[++cnt]=i,mu[i]=-1;
 49       for(LL j=1;j<=cnt && i*p[j]<N;j++)
 50         {
 51         v[i*p[j]]=1;
 52         if(i%p[j]==0)break;
 53         mu[i*p[j]]=-mu[i];
 54         }
 55       }
 56     for(LL i=1;i<N;i++)
 57       mu[i]+=mu[i-1];
 58     #undef LL
 59 }
 60 #define LL __int128
 61 LL f(LL a,LL b,LL c,LL n)
 62 {
 63     LL m=(a*n+b)/c;
 64     if(!a || !m)return 0;
 65     if(a>=c || b>=c)return n*(n+1)/2*(a/c)+(b/c)*(n+1)+f(a%c,b%c,c,n);
 66     return n*m-f(c,c-b-1,a,m-1);
 67 }
 68 LL check(Frac k)
 69 {
 70     LL res=0;
 71     for(LL i=1;i<=n;i++)
 72       {
 73       LL j=n/(n/i);
 74       res+=(mu[j]-mu[i-1])*f(k.p,0,k.q,n/i);
 75       i=j;
 76       }
 77     return res;
 78 }
 79 LL ceil(LL x,LL y){return (x-1)/y+1;}
 80 #undef LL
 81 void Find(Frac k)
 82 {
 83     #define LL long long
 84     Frac ans={1,1};
 85     for(LL i=1;i<=n;i++)
 86       ans=min(ans,{ceil(k.p*i,k.q),i});
 87     ans.simp();
 88     printf("%lld/%lld\n",(LL)ans.p,(LL)ans.q);
 89 }
 90 void init()
 91 {
 92     scanf("%lld%lld",&n,&k);
 93     Frac l={1,n},r={1,1},mid;
 94     while(l+(Frac){1,n*n}<=r)
 95       {
 96       mid=(l+r)/2;
 97       if(check(mid)<k)l=mid;
 98       else r=mid;
 99       }
100     Find(l);
101 }
102 int main()
103 {
104     //freopen("test.in","r",stdin);
105     //freopen("test.out","w",stdout);
106     pretype();
107     scanf("%lld",&T);
108     while(T--)init();
109     return 0;
110 }
View Code

 

posted @ 2019-07-23 02:04  DeaphetS  阅读(854)  评论(3编辑  收藏  举报