题目链接:https://vjudge.net/problem/POJ-2773
题意:给定m,k,求与m互质的第k个数。
思路一:利用gcd(a,b)=gcd(b*t+a,b)知道,与m互质的数是以m为周期分布的,这样可以先枚举小于m的所有与m互质的数,利用周期就可以得到第k小的数了,这样复杂度为O(T*m),比较大,但也能过,2439ms,代码实现相对简单。
AC代码:
#include<cstdio> using namespace std; int gcd(int a,int b){ return b?gcd(b,a%b):a; } int m,k,a[1000005],cnt; int main(){ while(~scanf("%d%d",&m,&k)){ cnt=0; for(int i=1;i<=m;++i) if(gcd(i,m)==1) a[++cnt]=i; printf("%d\n",((k-1)/cnt)*m+a[(k-1)%cnt+1]); } return 0; }
思路二:一般看到求第k个数可以想到二分思想,我们可以在int范围内二分答案,每次二分到mid时,要得到[1,mid]区间内与m互质的数的个数才行。求与m互质的数的个数,很明显求不互质的数的个数要方便,可以通过容斥转换为计算[1,mid]中与m不互质的数的个数,即通过枚举m的所有约数(预先通过唯一分解定理打表得到m的所有质因数,设有cnt个质因数,然后就有2^cnt-1个约数组合),通过约数中质因数数目的奇偶决定加或减即可,复杂读小很多。0ms通过。
AC代码:
#include<cstdio> using namespace std; typedef long long LL; int m,k,cnt,fac[50],ans; void init(){ cnt=0; int tmp=m; for(int i=2;i*i<=m;++i) if(tmp%i==0){ fac[cnt++]=i; while(tmp%i==0) tmp/=i; } if(tmp!=1) fac[cnt++]=tmp; } int getnum(int x){ int ret=x; for(int i=1;i<(1<<cnt);++i){ int t1=1,t2=0; for(int j=0;j<cnt;++j) if(i&(1<<j)) t1*=fac[j],++t2; if(t2&1) ret-=x/t1; else ret+=x/t1; } return ret; } int main(){ while(~scanf("%d%d",&m,&k)){ init(); int l=1,r=2147483647,mid; while(l<=r){ mid=(l+r)>>1; int tmp=getnum(mid); if(tmp<k) l=mid+1; else if(tmp>k) r=mid-1; else ans=mid,r=mid-1; } printf("%d\n",ans); } return 0; }