题目链接:http://poj.org/problem?id=2773
题解:
方法一:
求第k个与m互质的数。 如果 gcd(a,b) = 1, 那么成a与b互质。 注:1和任何数都互质。
根据唯一分解定理: 对一个数进行因式分解, 最终必定能分解为多个素数相乘的式子, 且这个式子是唯一的(1除外)。
1.那么我们可以m进行素数分解, 并记录它由哪些素数构成。 如果一个数的分解式中不含有构成m的素数, 那么这个数就与m互素。
2.然后二分答案ans, 如果在ans范围之内, 有>=k个与m互素的数, 那么就缩小范围, 否则扩大范围。
3.那么怎么知道在ans范围内, 有多少个数有m互素呢? 用容斥原理。(没学啊啊, 难以解释)
方法二:
可知 gcd(a+b*k, b) = gcd(b, a%b), gcd(a, b) = gcd(b, a%b), 所以 gcd(a+b*k, b) = gcd(a, b), k为常数。
这表明了:对于与b互素的数,他们对b取模的余数会周期性出现。 那么我们就只需要计算出在b的范围内, 与b互素的数有哪些就可以了。
然后看第k个与b互素的数是在第几个周期的第几个就可以了。(注意:刚好在周期末时, 需要特判)。
代码1(方法一):
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <queue>
#include <sstream>
#include <algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
typedef long long LL;
const double eps = 1e-6;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+7;
const int maxn = 1e5+10;
LL m, k;
LL fac[maxn], sum;
void init()
{
sum = 0;
LL tmp = m;
for(LL i = 2; i*i<=tmp; i++) //官方版的唯一分解定理
if(tmp%i==0)
{
fac[sum++] = i;
while(tmp%i==0) tmp /= i;
}
if(tmp>1) fac[sum++] = tmp;
// for(LL i = 2; i<=tmp; i++) //自己所理解的唯一分解定理。
// if(tmp%i==0)
// {
// fac[sum++] = i;
// while(tmp%i==0) tmp /= i;
// }
}
LL test(LL tmp)
{
if(m==1) return tmp;
if(tmp==1) return 1;
LL ret = 0;
for(LL s = 1; s < (1<<sum); s++)
{
LL p = 1, cnt = 0;
for(LL j = 0; j<sum; j++)
if(s&(1<<j))
{
p *= fac[j];
cnt++;
}
ret += (cnt&1)?(tmp/p):(-tmp/p);
}
return tmp - ret;
}
void solve()
{
LL l = k, r = LNF;
while(l<=r)
{
LL mid = (l+r)>>1;
if(test(mid)>=k)
r = mid - 1;
else
l = mid + 1;
}
printf("%lld\n", l);
}
int main()
{
while(scanf("%lld%lld",&m,&k)!=EOF)
{
init();
solve();
}
return 0;
}
代码2(方法一):
ll n, k;
ll factor[MAXN], sz;
ll sum, mid;
void prime_factor(ll n) {
sz = 0;
for (ll i = 2; i*i <= n; i++) if (n%i == 0) {
factor[sz++] = i;
while (n%i == 0) n /= i;
}
if (n > 1) factor[sz++] = n;
}
void dfs(ll id, ll step, ll num) {
if (id == sz) {
if (step != 0) {
if (step & 1) sum += mid / num;
else sum -= mid / num;
}
return;
}
dfs(id + 1, step, num);
if (num*factor[id] <= mid)
dfs(id + 1, step + 1, num*factor[id]);
}
int main() {
while (cin >> n >> k) {
prime_factor(n);
ll l = k, r = 1e18, ans = l;
while (l <= r) {
mid = (l + r) / 2;
sum = 0;
dfs(0, 0, 1);
if (mid - sum >= k) r = mid - 1, ans = mid;
else l = mid + 1;
}
cout << ans << endl;
}
return 0;
}
方法二:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <cmath>
#include <map>
#include <set>
#include <queue>
#include <sstream>
#include <algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
typedef long long LL;
const double eps = 1e-6;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+7;
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 = 0;
for(int i = 1; i<=m; i++)
if(gcd(i,m)==1)
pri[sum++] = i;
if(k%sum)
cout<< (k/sum)*m+pri[(k%sum-1)] <<endl;
else
cout<< (k/sum-1)*m+pri[sum-1] <<endl;
}
return 0;
}