题意: 求与m互质的第k个数。
我们可以找到规律,
1 ,2 ,3 ,4, 5, 6 ,7,8,9, 11,12,13,14 与5互质的数 x*m + 小于5与互质的数。
gcd(b×t+a,b)=gcd(a,b) (t为任意整数)
则如果a与b互素,则b×t+a与b也一定互素,如果a与b不互素,则b×t+a与b也一定不互素
故与m互素的数对m取模具有周期性
所以把小于m与m互质的数标记出来即可。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
#define FF freopen("Input.txt","r",stdin)
#define Debug puts("I'm here")
#define mem(x,y) memset(x,y,sizeof(x))
const int N=1e6+100;
int prime[100000];//保存1000以内的素数
int num; //素数的个数
int isprime[N];
int flag[N]; //flag[i]表示i是否与当前m互质
inline void get_prime() //筛出素数
{
int i,j;
for(i=2;i<N;i++)
{
if(isprime[i]==0) prime[num++]=i;
for(j=0;j<num&&prime[j]*i<N;j++)
{
isprime[prime[j]*i]=1;
if(i%prime[j]==0) break;
}
}
}
inline int euler(int n)//欧拉函数求出与m互质的总个数,并标记与m不互质的数。
{
int res=n;
int m=n;
for(int i=0;i<num&&prime[i]*prime[i]<=n;i++)
{
if(n%prime[i]==0)
{
res=res/prime[i]*(prime[i]-1);
while(n%prime[i]==0) n/=prime[i];
for(int j=prime[i];j<=m;j+=prime[i])
flag[j]=1;
}
}
if(n>1)
{
res=res/n*(n-1);
for(int j=n;j<=m;j+=n)
flag[j]=1;
}
return res;
}
inline int Find(int k)
{
int cnt=0;
for(int i=1;i<N;i++)
{
if(flag[i]==0)
cnt++;
if(cnt==k)
return i;
}
}
int main()
{
int m,k,i;
get_prime();
while(~scanf("%d%d",&m,&k))
{
mem(flag,0);
int len=euler(m);
int x,y;
y=k%len;
if(y==0) y=len;
x=k%len==0?k/len-1:k/len;
printf("%I64d\n",(__int64)(m*x+Find(y)));
}
return 0;
}
法二:用容斥求【1,mid】的内与m不互质的数的和,二分枚举mid。
先预处理出m的质因子。
容斥:集合中所有数 - 1个质因子 + 2个质因子乘积的 - 3个质因子乘积的+4个质因子乘积的 -、、、、、、。
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N=20;
int a[N],num,mid,sum;
void dfs(int i,int w,int flag)//容斥原理.
{
for(;i<num;i++)
{
int p=a[i]*w;
sum+=flag*(mid/p);
dfs(i+1,p,-flag);
}
return ;
}
int main()
{
int m,k;
while(~scanf("%d%d",&m,&k))
{
if(m==1)
{
printf("%d\n",k);
continue;
}
num=0;
for(int i=2;i<=m;i++) //求出所有质因子
{
if(m%i==0)
{
a[num++]=i;
while(m%i==0) m/=i;
if(m==1) break;
}
}
if(m>1) a[num++]=m;
int l=1,r=0x7fffffff,ans=0;
while(l<=r)//二分枚举
{
mid=(l+r)>>1;
sum=0;
dfs(0,1,1);
sum=mid-sum;
if(sum>=k)
{
r=mid-1;
if(sum==k) ans=mid;//不能break,
}
else l=mid+1;
}
printf("%d\n",ans);
}
return 0;
}