Description
Input
Output
Sample Input
2006 1 2006 2 2006 3
Sample Output
1 3 5
Source
#include <iostream> #include <cmath> #include <algorithm> #include <cstdio> #define ll long long int #define MAXN 1010 using namespace std; bool isPrime[MAXN]; int Prime[MAXN],PriNum[MAXN],cnt,tot; int m,k; ll res; ll gcd(ll a,ll b) { if(a%b==0) return b; return gcd(b,a%b); } void GetPrime() //筛素数,打表获得1000以内的质数 { cnt=0; for(int i=1;i<MAXN;i++) isPrime[i]=true; for(int i=2;i<MAXN;i++) if(isPrime[i]) { Prime[++cnt]=i; for(int j=i*2;j<MAXN;j+=i) //i的倍数都不是素数 isPrime[j]=false; } } void Cal(int cur) //对cur分解质因数,最终结果保存在PriNum数组中,无重复质因数 { tot=0; for(int i=1;Prime[i]*Prime[i]<=cur;i++) { if(!(cur%Prime[i])) //发现新的质因数 { PriNum[++tot]=Prime[i]; //记录质因数 while(!(cur%Prime[i])) cur/=Prime[i]; } } if(cur!=1) PriNum[++tot]=cur; //剩余的不是1,则新增一个质因数 } void dfs(ll hav,int cur,int num,ll va) //容斥原理求[1,va)中与m互质的数的个数,num为偶数则加,奇数则减 { if(cur>tot||(hav>va)) return; for(int i=cur;i<=tot;i++) { ll temp=hav*PriNum[i]; if(num&1) //这一步是要减去的 res-=va/temp; else res+=va/temp; dfs(temp,i+1,num+1,va); } } void solve(ll cur) //求出1~cur之间与m互质的数的个数 { res=cur; for(int i=1;i<=tot;i++) { res-=cur/PriNum[i]; dfs(PriNum[i],i+1,2,cur); } } int main() { GetPrime(); //先打表求出需要的素数 while(~scanf("%d%d",&m,&k)) { ll ans,L,R,M; L=1,R=(1LL<<62LL); //初始化二分边界为[1,+∞) Cal(m); while(L<=R) //二分猜答案 { M=(L+R)>>1; solve(M); //获得[1,M]之间与m互质的数的个数 ll temp=res; if(temp<k) //数的个数不够,需要放大范围 L=M+1; else if(temp==k) //数的个数刚刚好,则二分查找最后一个互质的数 { if(gcd(M,m)==1) //最后一个数刚好与m互质,找到了答案 { ans=M; break; } R=M-1; } else R=M-1; //否则,数太多了,需要缩小范围 } printf("%lld\n",ans); } return 0; }然后叉姐提供给我一个简洁明了的公式,此题的答案就是sum_{d | n} miu(d) [m / d],仔细琢磨下发现这个公式实在精辟,神得不能再神!