思路:
我们知道b = [a, a + m], 我们需要满足Gcd(a, m) = Gcd(b, m), 假设g = Gcd(a, m),那么Gcd(a, m) = Gcd(a + k * g, m)(a + k * g ∈ b),两边同除以g,
Gcd(a / g, m / g) = Gcd(a / g + k, m / g) = g / g = 1 < ====> Gcd(b / g, m / g) = 1, 说明我们需要在b / g = [(a + g - 1) / g, (a + m) / g] 找到与 m / g互质的个数即可。
这里需要用到欧拉函数的相关知识,不会的学要自己学习。这样的话我们只需要求出( b/g右区间到1与m/g互质的个数) - (b/g左区间减一与m/g互质的个数)即可
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <queue> #include <string> #include <map> #include <set> #define LL long long using namespace std; long long Gcd(LL a, LL b) { return b == 0 ? a :Gcd(b, a % b); } void solve() { int T; scanf("%d", &T); while(T--) { long long a, m, x, y, k, g, cp_k; scanf("%lld%lld", &a, &m); g = Gcd(a, m); x = (a + g - 1) / g; y = (a + m) / g; k = m / g; ///printf("x = %lld y = %lld k = %lld\n", x, y, k); long long res[2]; res[0] = y; cp_k = k; for(LL i = 2; i * i <= k; ++i) { if(cp_k % i == 0) { res[0] = res[0] / i * (i - 1); while(cp_k % i == 0) { cp_k /= i; } } } if(cp_k > 1) res[0] = res[0] / cp_k * (cp_k - 1); ///cout << "43 lines" << endl; res[1] = x - 1; cp_k = k; for(LL i = 2; i * i <= k; ++i) { if(cp_k % i == 0) { res[1] = res[1] / i * (i - 1); while(cp_k % i == 0) { cp_k /= i; } } } if(cp_k > 1) res[1] = res[1] / cp_k * (cp_k - 1); ///cout << res[0] << " " << res[1] << endl; printf("%lld\n", res[0] - res[1]); } } int main() { solve(); return 0; }