Happy 2006
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 8447 | Accepted: 2777 |
Description
两个正整数如果称为互质,那么应满足这两数的最大公约数(GCD)为1。例如,1,3,5,7,9……都与2006互质。你现在的工作很容易:对于给定的整数m,找到第k个与M互质的元素,这些元素是以升序排序。
Input
输入包含多个测试案例。对于每个测试案例,它包含两个整数m(1 <= m <= 1000000), K (1 <= K <= 100000000).
Output
对于每个案例,输出第k大的与M互质的数,每个数占一行
Sample Input
2006 1
2006 2
2006 3
Sample Output
1
3
5
Source
Translation
【分析】:
这道题目首先想到二分一个数x,表示所求答案在区间[1,x]内,现在需要验证是否在区间[1,x]内有且仅有k个与M互质的数,即求[1,x]中与M互质的数的个数z。这里求z值需转换思想,因为求M的约数比求与M互质的数更快,而求约数可以用M分解因数后的值配上容斥原理解决。那么首先将M分解因数p1-pn,存在数组mult中。
因为x在区间[1,y]中的倍数有y/x<向下取整>个,那么根据容斥原理,在区间[1,x]内是M的倍数的数为:
即所有数集合-M以内所有一个素因子构成的因子的个数+M以内所有两个素因子构成的因子的个数-M以内所有三个素因子构成的因子的个数...,那么与M互质的个数就为X-区间[1,x]内是M的倍数的数。
注意:二分验证完后,不应急于结束二分,因为这里是要求一个下限值x,即在区间[1-x)中与M互质的数的数量刚好小于K,因此应继续让他二分下去
【代码】:
/*
Memory: 8000K Time: 63MS
Language: C++ Result: Accepted
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define MAX 1000001
#define IMAX 2147483647
long long M,K,ans,tot=0,change=1;
long long mult[MAX],use[MAX];
void divide1(long long x)
{
memset(mult,0,sizeof(mult));
for(long long i=2;i*i<=x;i++)
if(x%i==0)
{
mult[++tot]=i;
while(x%i==0) x/=i;
}
if(x>1) mult[++tot]=x;
}
long long check(long long x)
{
long long sum=0;
for(long long num=1;num<(1<<tot);num++)//二进制枚举的容斥
{
long long nowsum=1,cnt=0;
for(int i=1;i<=tot;i++)
{
if(num&(1<<(i-1)))
{
cnt++;
nowsum*=mult[i];
}
}
if(cnt%2) sum+=x/nowsum;
else sum-=x/nowsum;
}
return (x-sum);
}
int main()
{
//freopen("input.in","r",stdin);
//freopen("output.out","w",stdout);
while(scanf("%lld%lld",&M,&K)!=EOF)
{
tot=0;
divide1(M);
long long left=1,right=IMAX;
while(left<=right)
{
long long middle=(left+right)/2;
long long now=check(middle);
if(now>=K)
{
right=middle-1;
if(now==K) ans=middle;
}
else left=middle+1;
}
printf("%lld\n",ans);
}
//system("pause");
return 0;
}