如何有效地获得给定范围内的除数和

How to get the sum of divisors in a given range efficiently?

本文关键字:范围内 有效地      更新时间:2023-10-16

我试图在给定范围 a、b a <= b 中找到c的所有除数之和。

我试图从 a 循环到 b 并求和 c 的所有除数,但这似乎效率低下,因为 a 和 b 之间的绝对差可以是 10^9。有没有办法降低这种方法的时间复杂度?

int a, b, c;
cin >> a >> b >> c;
long long sum = 0;
for (int i = a; i <= b; i++) {
    if (i % c == 0) {
       ans += i;
    }
}
cout << sum << endl;

注意:问题尚不清楚我们是否需要对除数(在描述中)或可分整数(在代码示例中)求和。答案总结了可分割的项目。

这很简单。

  • from,最小值,使得from % c == 0 && from >= a
  • to,最大值,使得to % c == 0 && to <= b

.

 int n = (to - from) / c + 1;
 return n * (to + from) / 2;

返回to - from + c .当to可能溢出您的类型并且from可能下溢时,请注意边界条件。

要查找from请执行以下操作:

if (c < 0) c *= -1;  // works unless c == MIN_INT
if (a % c == 0)
   from = a;
else if (a >= 0)
   from = (a / c * c) + c
else 
   from = a / c * c;

同样适用于to,但考虑到我们需要向下舍入而不是向上舍入的事实。

另外,需要单独处理a > b的情况。

编辑

下面是没有循环、递归或容器的完整代码。它在 O(1) 中运行:

int a, b, c;
std::cin >> a >> b >> c;
if (!std::cin) {
   std::cout << "input errorn";
   return 0;
}
if (c < 0) c*= -1;
const int from = [a,c] {
   // no rounding needed
   if (a % c == 0) return a;
   // division rounds down to zero
   if (a > 0) return (1 + a / c) * c;
   // division rounds up to zero
   return a / c * c;
}();
const int to = [b,c] {
   // no rounding needed
   if (b % c == 0) return b;
   // division rounds down to zero
   if (b > 0) return (b / c) * c;
   // division rounds up to zero
   return (b / c - 1) * c;
}();
int64_t sum = 0;
if (from <= to)
{
 const int n = (to - from) / c + 1;
 sum = n * (to + from) / 2;
}
std::cout << sum << 'n';

首先确定所有作为 c 除数的素数。 这将给你留下一个数字列表[w,x,y,z...]。 然后保留此列表中也是除数的所有整数倍的哈希表集。

int a, b, c;
cin >> a >> b >> c;
long long sum = 0;
std::vector<int> all_prime_factors = // Get all prime factors of c
std::unordered_set<int> factorSet;
for (int primefactor : all_prime_factors)
{
    int factor = primefactor;
    while (factor <= b)
    {
        if (factor % c == 0)
            factorSet.insert(factor);
        factor += primefactor;
    }
}
for (int x : factorSet)
{
    sum += x;
}
cout << sum << endl;