统计因子/枚举倍数
给你两个整数数组 和 ,长度分别为 和 。同时给你一个正整数 。
如果 可以被 整除,则称数对 为 优质数对 ( )。
返回 优质数对 的总数。
统计因子
我们知道, 如果 可以被 整除, 则有
即 是 的因子, 因此我们可以先统计 因子的个数, 再遍历 看它是否在 的因子里面.
- 我们知道统计一个数因子的算法的时间复杂度是 的.
类比判断质数的那个:
-
如果一个数 可以 整除 , 那么 是 的一个因子, 但是同时 也是 的一个因子, 所以我们在 就可以一次找到所有的因子!
-
特别注意的是: 如果 时候, 会重复统计, 因此需要去掉一个!
代码如下, (小优化: (左右同时平方嘛~))
unordered_map<int, int> cnt;
int x = 114514; // 统计 114514 的因子
for (int d = 1; d * d <= x; d++) {
if (x % d)
continue;
cnt[d]++;
if (d * d < x)
cnt[x / d]++;
}
总的代码如下: (小优化: a[i] % (b[i] * k) <=> (a[i] / k) % (b[i]), 这样可以减少枚举范围)
class Solution {
public:
long long numberOfPairs(
vector<int>& nums1, vector<int>& nums2, int k) {
/*
(a[i] / k) % (b[i]) == 0
枚举 a[i] / k 的 因子
*/
unordered_map<int, int> cnt;
for (int it : nums1) {
if (it % k)
continue;
it /= k;
for (int i = 1; i <= sqrt(it); ++i) {
if (it % i == 0) {
++cnt[i];
if (i * i < it)
++cnt[it / i];
}
}
}
long long res = 0;
for (int it : nums2)
res += cnt.count(it) ? cnt[it] : 0;
return res;
}
};
时间复杂度: 其中 是 的长度, 是 的长度,
枚举倍数 (调和级数枚举)
-
先去重 和 到 和 中, 记 中元素最大值为 .
-
枚举 的元素 , 然后枚举 的倍数: 直到 , 然后看看这些值有那些是在 的, 累加到 中.
class Solution {
public:
long long numberOfPairs(
vector<int>& nums1, vector<int>& nums2, int k) {
/*
(a[i] / k) % (b[i]) == 0
b[i] 的倍数, 使其不超过 max(a[i] / k)
*/
unordered_map<int, int> cnt1, cnt2;
for (int it : nums1) {
if (it % k)
continue;
++cnt1[it / k];
}
if (!cnt1.size())
return 0;
int maxx = 0;
for (auto [i, _] : cnt1)
if (i > maxx)
maxx = i;
for (int it : nums2)
++cnt2[it];
long long res = 0;
for (auto [i, v] : cnt2) {
int s = 0;
for (int j = i; j <= maxx; j += i) { // 枚举倍数
s += cnt1.count(j) ? cnt1[j] : 0;
}
res += (long long) s * v;
}
return res;
}
};
时间复杂度: , 其中 是 的长度, 是 的长度, .
其中, 由调和级数推得.
证明:
最坏情况下: (全部只出现一次)
并且由于 哈希表 肯定不能有重复的 , 那么假设
那么从 枚举 的次数 为 次
即枚举 需要: 即 即 然后再严谨一些? 即 一共有 个, 并且 是常数, 实际上是我们的 , 时间复杂度不理会 的底数, 故整理得