|
算法分析:
题意:
给你m和k,求第k个与m互质的数。
分析:
第一种做法:时间:1750ms
在1~m之间有phi[m](phi[m]是m的欧拉函数)个数与m互质,而在m+1~2*m之间也有phi[m]个数与m互质,(假设2与m互质,2+m肯定与m互质,所以与m互质的数挨个相加即可)且在n*m+1~(n+1)*m之间也有phi[m]个数与m互质,并且这phi[m]个数与在1~m之间的phi[m]个数是相互对应的。
所以我们先求出1~m之间的phi[m],然后直接跳到相应的区间,然后开始枚举,优化了时间。
第二做法:(第一种做法的公式版)时间:2470ms
gcd(a+b*k, b) = gcd(b, a%b), gcd(a, b) = gcd(b, a%b), k为常数。
这表明了:对于与b互素的数,他们对b取模的余数会周期性出现。 那么我们就只需要计算出在b的范围内, 与b互素的数有哪些就可以了。
然后看第k个与b互素的数是在第几个周期的第几个就可以了。(注意:刚好在周期末时, 需要特判)
做法三:点这里 24ms
代码实现:
方法一:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
using namespace std;
typedef long long ll;
const int N=1000010;
int prime[N],mark[N];//prime是素数数组,mark为标记不是素数的数组
int tot,phi[N];//phi为φ(),tot为1~i现求出的素数个数
void getphi(){
phi[1]=1;//φ(1)=1
for(int i=2;i<=N;i++){//从2枚举到N
if(!mark[i]){//如果是素数
prime[++tot]=i;//那么进素数数组,指针加1
phi[i]=i-1;//根据性质1所得
}
for(int j=1;j<=tot;j++){//从现求出素数枚举
if(i*prime[j]>N) break;//如果超出了所求范围就没有意义了
mark[i*prime[j]]=1;//标记i*prime[j]不是素数
if(i%prime[j]==0){//应用性质2
phi[i*prime[j]]=phi[i]*prime[j];break;
}
else phi[i*prime[j]]=phi[i]*phi[prime[j]];//应用性质3
}
}
}
ll gcd(ll a,ll b) { return b == 0 ? a : gcd(b, a % b); }
int main()
{
int n,m;
getphi();
while(scanf("%d%d",&n,&m)!=EOF)
{
int k=m/phi[n];
if(m%phi[n]==0) k--; //k记录前面有多少个区间,区间是从0开始的
m=m-k*phi[n]; //m表示在第k个区间第几个数
ll num=n*k+1; //k区间第一个数
ll i,ans;
for(i=num;m!=0;i++)
if(gcd(n,i)==1)
{
ans=i;
m--;
}
printf("%lld\n",ans);
}
}
方法二:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
using namespace std;
const int maxn = 1e6+10;
int pri[maxn];
int gcd(int a, int b)
{
return b==0?a:gcd(b, a%b);
}
int main()
{
int m, k, sum;
while(cin>>m>>k)
{
sum=1;
for(int i = 1; i<=m; i++) //保存所有欧拉函数
if(gcd(i,m)==1)
{
pri[sum++] = i;
}
sum--;
if(k%sum)
cout<<(k/sum)*m+pri[k%sum] <<endl;
else
cout<<(k/sum-1)*m+pri[sum] <<endl;
}
return 0;
}