求因数和模运算的和

huangapple go评论60阅读模式
英文:

sums of divisors and modulo operator

问题

问题包括一个函数S(i),该函数对某个数字的所有可能的除数进行求和,所以S(18)=1+2+3+6+9+18=39。鉴于此,我们必须计算对于从1到n的值的S(i)%MOD的总和,其中n可以高达10^12。

在这里,我使用了每个i的除数出现次数(通过使用公式occurrence = q = floor(n/i))。然后使用公式floor(n/q)来查看最后一个与另一个数字共享该出现次数的数字i。最后,我使用等差数列求和公式来对所有共享相同出现次数的除数进行求和,并将它们乘以q,将i的值移动到已知其出现次数的值的数量跳过的位置。我的问题是,在接近非常大的数字时,代码会失败,提供错误答案并未通过测试。

#include <iostream>
#include <cmath>

using namespace std;

const int MOD = 1000000007;

int main() {
    long long n;
    cin >> n;

    long long ans = 0;
    long long q = 0;

    long long a = 0;
    long long s = 0;
    long long ultimo_i = 0;

    for (long long i = 1; i <= n;) {
        q = static_cast<long long>(floor(n / i));
        ultimo_i = static_cast<long long>(floor(n) / q);
        a = (ultimo_i - i + 1);
        s = ((i + ultimo_i) * a) / 2;
        ans += (s * q);
        i += a;
    }

    ans %= MOD;

    cout << ans << endl;

    return 0;
}
英文:

The problem consists of a function S(i) which sums all possible divisors of a certain number, so S(18)=1+2+3+6+9+18=39. given this we must calcualte the sum of S(i)%MOD for values of 1 to n. with n being up to 10^12.

Here I used the occurence of the divisors for each i (by using the formula occurence = q = floor(n/i). and the formula floor(n/q) to see the last number i that shares that occurence with another number. Lastly I use the arithmetic sum formula to sum all divisors that share the same occurence, and multiply them by q, moving the i value by the number of values skipped as we already now their occurence. My problem is that nearing very large numbers the code fails, providing a wrong answer and faioing the test.

#include &lt;iostream&gt;
#include &lt;cmath&gt;

using namespace std;

const int MOD = 1000000007;

int main() {
    long long n;
    cin &gt;&gt; n;

    long long ans = 0;
    long long q = 0;
 
    long long a = 0;
    long long s = 0;
    long long ultimo_i = 0;

   for (long long i=1;i&lt;=n;) {
        q = static_cast&lt;long long&gt;(floor(n / i));
        ultimo_i = static_cast&lt;long long&gt;(floor(n) / q);
        a = (ultimo_i - i + 1);
        s = ((i+ultimo_i) * a) / 2;
        ans += (s * q);
        i += a;
    }
    
    ans %= MOD;

    cout &lt;&lt; ans &lt;&lt; endl;

    return 0;
}

答案1

得分: 1

预期结果无法表示为long long,它太大了,实际上,OP的代码应该输出该结果对1,000,000,007取模。

为了获得正确的值,我们应该在计算的每个合理步骤中使用模运算属性

基本上意味着你必须在每个值和中间结果(在每个求和或乘法之后)上应用% 1,000,000,007LL,这些值和中间结果将超出所使用类型的范围。

请注意,选择模数的特定值,除了是质数外,还足够小,可以安全地作为long long的平方(但太大以至于不能升到3次方),但(一般情况下)不能作为int(就像OP的代码中的情况一样),在大多数系统中,int是32位宽的类型,现在。

要计算/ 2,我们需要找到相对于所述模数的模乘逆。换句话说,我们需要找到x,使得

2 ∙ *x* ≡ 1(mod 1,000,000,007)

除了使用适当的形式方法之一,我们也可以观察到

(1,000,000,007 + 1)/ 2 = 500'000'004   -->   2 ∙ 500'000'004 ≡ 1(mod 1,000,000,007)

因此,要除以2,我们只需乘以 500'000'004,然后取模(% 1,000,000,007)。

在发布的代码中还存在另一个问题:

long long n;
long long q;
// ...

for (long long i = 1; i <= n;) {
    q = static_cast<long long>(floor(n / i));
    //  ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^

    // ...
}

没有必要使用std::floor,它返回一个double。该操作,n / i,已经是整数除法,产生具有类型long long的⌊n/i⌋。整数到浮点再到整数的转换只是浪费资源。

英文:

The expected result can't be represented as a long long, it's far too big and in fact OP's code is supposed to output that result modulo 1,000,000,007.

To obtain the correct value, though, we should use modular arithmetic properties at each sensible step of the calculation.

It basically means that you have to apply % 1,000,000,007LL at each value and intermediate result (after every sum or multiplication) that is going to outgrow the range of the used type.

Note that the particular value chosen for the modulo, other than beeing a prime is also small enough to be safely squared as a long long (but too big to be elevated to the power 3), but (in general) not as an int (like in OP's code), which is a 32-bit wide type in most systems, nowadays.

To calculate / 2, we need to find the modular multiplicative inverse of 2 with respect to the said modulus. In other words, we need to find x such that

2 ∙ *x* ≡ 1  (mod 1,000,000,007)

Instead of using one of the proper formal methods, we could just observe that

(1,000,000,007 + 1) / 2 = 500&#39;000&#39;004   --&gt;   2 ∙ 500&#39;000&#39;004 ≡ 1  (mod 1,000,000,007)

So that, to divide by 2, we just have to multiply by 500'000'004 and then take the modulo (% 1,000,000,007).

There's another issue in the posted code:

long long n;
long long q;
// ...

for (long long i=1;i&lt;=n;) {
    q = static_cast&lt;long long&gt;(floor(n / i));
    //  ^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
    
    // ...
}

There's no need to use std::floor, which returns a double. That operation, n / i, is already an integer division, producing ⌊n/i⌋ with type long long. The conversion integral to floating-point to integral again is just wasteful.

huangapple
  • 本文由 发表于 2023年6月16日 05:08:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76485523.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定