题目大意
题目传送门
U611329 好数
题目描述
如果一个正整数 \(x\) 满足 \(x = an + b (n \in \mathbb{N}^+)\)(\(a, b\) 为给定的常数),则称 \(x\) 为「好数」。
如果一个「好数」不能被除了自己以外的任何「好数」整除,则称这个数为「很好数」。
你的任务是求出前 \(m\) 个「好数」中有多少个「很好数」。
思路
看到是要将所有的好数中是很好数的个数求出来,相当于筛掉一些非很好数。所以可以用类似于质数筛的筛法来解决这个问题。
设第 \(n\) 个「好数」为「很好数」,则这个数为 \(x = an + b\)。现在需要快速的在是好数的数中筛掉非很好数即筛掉 \(x\) 的倍数。
设第 \(n'\) 好数为非很好数且为 \(x\) 倍数,设 \(an' + b = x * k\) 其中 \(k\) 为整数。
将 \(x = an + b\) 代入式子中,得 $$an' + b = (an + b) * k$$
设 \(d = gcd(a, b)\),则 \(a = a'd\),\(b = b'd\),且 \((a', b') = 1\),再次代入式子可以发现
由于 \((a', b') = 1\),所以 \(a'\) 整除 \(k - 1\)。
不妨设 \(j\) 使 \(a'j = k - 1\),所以 \(k = a'j + 1\),代入原式
于是就可以枚举 \(j(1 \le j\ \&\ j * (na' + b') + n \le m)\),筛掉不合法的数了。
时间复杂度 \(O(n log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;const long long N = 10000010;
long long m, a, b;
bool vis[N];long long gcd(long long x, long long y) {if (y == 0) return x;return gcd(y, x % y);
}int main() {cin >> m >> a >> b;long long d = gcd(a, b);long long pa = a / d, pb = b / d, ans = 0;for (long long i = 1; i <= m; i++)if (!vis[i]) {ans++;long long x = pa * i + pb;for (long long j = 1; j * x + i <= m; j++)vis[j * x + i] = true;}cout << ans << endl;return 0;
}