题目链接:Same GCDs
大致题意
给定两个数n和m, 其中保证n < m.
问存在多少个整数x(0 ≤ x < m), 使得gcd(a, m) = gcd(a + x, m);
解题思路
本题是对欧拉函数的考察. 难点在于给定等式的变形.
首先因为a和m都是给定的, 所以不妨令常量 d = gcd(a, m) = gcd(a + x, m);
由于gcd(a, b) = gcd(b, a % b), 所以 gcd(a + x, m) = gcd((a + x) % m, m) = d
把**(a + x)记为x’, 因为x可以取[0, m), 而a ≥ 1, 因此(a + x) % m一定可以取遍[0, m), 因此x’ 可以取遍[0, m)**
<==> gcd(x’ % m / d, m / d) = 1 形式相当于 gcd(x’ % n, n). 同理得出x’ % n一定可以取遍[0, n).
由于欧拉函数的定义是求[1, n]之中, 所有与n互质的数的个数, 而本题所得到的区间是[0, n), 需要进一步转换.
首先当x’ % n == 0时, 是没有意义的, 区间可以变为[1, n).
此时若n == 1, 那么区间应当变为[1, 1], 若n != 1, 那么n与其自身一定不互质, 则区间可以变为[1, n].
综上所述, 可以用欧拉函数来计算上式成立的次数, 相当于得到x的可取值个数.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll fact(ll n) { //求单个数欧拉函数模板, 复杂度O(√n)
ll res = n;
for (ll i = 2; i <= n / i; ++i) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) res = res / n * (n - 1);
return res;
}
int main()
{
int t; cin >> t;
while (t--) {
ll a, m; cin >> a >> m;
ll d = gcd(a, m);
ll res = fact(m / d);
printf("%lld\n", res);
}
return 0;
}
菜鸡不会数论, 我乱讲的, 如果有地方说的不对麻烦评论直接指出.