http://162.105.81.212/JudgeOnline/problem?id=2773
题意:给定m, k(1 <= m <= 1000000), K(1 <= K <= 100000000), 询问第k个与m互质的数。
思路:看题目的数据范围是1000000,所以可以先用线性方法计算所有小于M的与M互质的数,储存到数组A[]中,并用tot记录总数。
对于K <= TOT ,直接找到A[K]即可。
对于K > TOT ,取M=5观察与它互质的数的情况:
1 1
2 2
3 3
4 4
5 6 = 1*5+1 = 1*5+A[1]
6 7 = 1*5+2 = 1*5+A[2]
7 8 = 1*5+3 = 1*5+A[3]
8 9 = 1*5+4 = 1*5+A[4]
9 11 = 2*5+1 = 2*5+A[1]
10 12 = 2*5+2 = 2*5+A[2]
11 13 = 2*5+3 = 2*5+A[3]
……
显然对于K > TOT 可以得到A[K] = X*M+A[Y],其中X = (K-1)/4,Y = (K-1)%4+1。
下面是代码,但是有点慢1900+ms。
#include<iostream> #include<cmath> using namespace std; const int maxn = 1000000; int m, k; int ans[maxn]; int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a%b); } int main() { int tot, i, j, t1, t2; int flag; while(scanf("%d%d",&m,&k) == 2) { tot = 0; flag = 0; for(i=1; i<=m; i++) { if(gcd(m, i) == 1) { ans[++tot] = i; if(k == tot) { printf("%d/n",i); flag = 1; break; } } } if(k > tot) { t1 = (k-1) / tot; t2 = (k-1) % tot + 1; printf("%d/n",t1*m + ans[t2]); } } return 0; }
解法二:也是上面的分析,但是要用到欧拉函数和整数分解来求解,加快了一点。转帖:
欧拉函数的应用。首先是求出m的欧拉函数值phi[m],可知区间[1, m - 1]中有phi[m]个数与m互质。同样,在区间[n*m + 1, (n + 1) * m]中必然也有phi[m]个数与m互质,并且这phi[m]个数与[1 - m - 1]的phi[m]个数是"一一对应"的。用反证法可以证明
假设gcd(a + b, b) != 1, 那么设gcd(a + b, b) = p, 则p是a + b和b的公共素因子的乘积,那么p一定是a, b的公共素因子的乘积,理由如下:
假设a + b = p*q, b = p*t,
a + p*t = p*q -> a = (q - t) * p(q>t)
a与b均含有p这个公约数,p != 1,p是a, b的公共素因子的乘积,这与gcd(a, b) = 1矛盾,所以假设错误。
因此知道[n*m + 1, (n + 1) * m]中必然也有phi[m]个数与m互质,并且这phi[m]个数与[1 - m - 1]的phi[m]个数是"一一对应"的。
所以只要通过k / phi[m]找到第n个区间,然后再枚举区间中的数就可以找到第k个与m互质的数。下面代码用了1500+ms;
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N = 1000000 + 10; int m, k; int phi[N]; inline int gcd(int a, int b) { int r; while (b) { r = a % b; a = b; b = r; } return a; } void getphi() { memset(phi, 0, sizeof (phi)); phi[1] = 1; for (int i = 2; i < N; ++i) { if (!phi[i]) { for (int j = i; j < N; j += i) { if (!phi[j]) phi[j] = j; phi[j] = phi[j] / i * (i - 1); } } } } int main() { getphi(); while (scanf("%d%d", &m, &k) != EOF) { int now = 0; now = k / phi[m]; if (k % phi[m] == 0) now--; k = k - now * phi[m]; now = now * m + 1; int temp; for (int i = now; k != 0; ++i) if (gcd(i, m) == 1) { temp = i; --k; } printf("%d/n", temp); } return 0; }
看了网上的解题报告,好像最快的要用容斥原理+二分;转帖下别人的思路以后再贴代码吧。
方法就是先求出m以内的所有素数(预处理),然后对m因式分解,m=p1^a1 * p2^a2 * ... * pt^at,然后分别记录下p1, p2, ..., pt。接着是二分k,对于每次二分,用容斥原理去求答案,比如与2不互素的有m / 2个,与3不互素的有m / 3个,与2 * 3 = 6不互素的有m / 6个,因此就是m / 2 + m / 3 - m / 6,显然,这个过程就是个DFS。用了47ms。