Happy 2006
Time Limit: 3000MS | Memory Limit: 65536K | |
Total Submissions: 8661 | Accepted: 2863 |
Description
Two positive integers are said to be relatively prime to each other if the Great Common Divisor (GCD) is 1. For instance, 1, 3, 5, 7, 9...are all relatively prime to 2006.
Now your job is easy: for the given integer m, find the K-th element which is relatively prime to m when these elements are sorted in ascending order.
Now your job is easy: for the given integer m, find the K-th element which is relatively prime to m when these elements are sorted in ascending order.
Input
The input contains multiple test cases. For each test case, it contains two integers m (1 <= m <= 1000000), K (1 <= K <= 100000000).
Output
Output the K-th element in a single line.
Sample Input
2006 1 2006 2 2006 3
Sample Output
1 3 5
Source
POJ Monthly--2006.03.26,static
第一步: 建立 1~N 的素数表;
↓
第二步:将 m 素因子分解;
↓
第三步:二分找答案;
↑
方法
(第三步的小步骤):dfs求不与 m互素的数(运用了 容斥原理).
具体原理见参考资料!
注意:其中 mid/n 表示 n~mid 内有多少个 是n的倍数的数字.(即与m 不互素的数)
其中的容斥原理说明:
比如: m=12, 13以内与它不互素的数 数目!
1,2,3,4,5,6,7,8,9,10,11,12
num=13/2+13/3-13/(2*3) 约等于8 (应该取整数!)
除以2 得到 13以内 2的倍数的数的数量,同理除以3.
但是两者加起来会 把有些 即是2的倍数又是3的倍数的数 重复算两次 所以要 减去!
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 1000005
#define inf 0x7fffffff
typedef __int64 LL;
LL prime[N],cnt=0;
bool isprime[N];
LL d[30],f;
void Init() //素数打表
{
memset(isprime,true,sizeof(isprime));
for(LL i=2;i<=N-5;i++)
{
if(isprime[i])
{
prime[cnt++]=i;
for(LL j=i*i;j<=N-5;j+=i)
{
isprime[j]=false;
}
}
}
}
void dfs(LL cur,LL now,LL mid,bool flag,LL &ans)
{
if(cur>=f) return; //一直处理到m的最后 一种 素因子为止!
dfs(cur+1,now,mid,flag,ans); //当前now不与d[cur] 求最小公倍数(即两者相乘)的情况!
LL n=now*d[cur]; //当前now 与 d[cur] 求最小公倍数(即两者相乘)的情况!
if(flag)
ans+=mid/n;
else
ans-=mid/n;
dfs(cur+1,n,mid,!flag,ans);
}
LL sum(LL mid)
{
LL ans=0;
dfs(0,1,mid,true,ans);
return mid-ans; //mid-ans 中 ans是不与m互素的数 ,所以要减去。
}
int main()
{
Init(); //打表
LL m,k;
while(~scanf("%I64d%I64d",&m,&k))
{
f=0;
for(LL i=0;i<cnt;i++) //m素因子分解
{
if(m%prime[i]==0)
{
d[f++]=prime[i];
while(m%prime[i]==0)
{
m/=prime[i];
}
}
if(m==1) break;
}
LL low=1,high=inf;
LL ans;
while(low<=high) //二分
{
LL mid=(high-low)/2+low;
LL temp=sum(mid);
if(k==temp)
ans=mid;
if(temp>=k)
high=mid-1;
else
low=mid+1;
}
printf("%I64d\n",ans);
}
return 0;
}