如果我们有两个数字,比如说a and b
那么我们如何才能找到sum of b%i where i ranges from 1 to a
的值?
一种方法是迭代从1到a的所有值,但是有没有有效的方法?
(优于O(n)?)
例如:如果a = 4且b = 5则需要ans = 5%1 + 5%2 + 5%3 + 5%4 = 4
感谢。
答案 0 :(得分:4)
对于i > b
,我们有b % i == b
,因此可以在恒定时间内轻松计算总和的一部分((a-b)*b
,如果a >= b
,则为0)。
i <= b
的部分仍有待计算(i == b
给出0,因此可以忽略)。你可以用O(sqrt(b))步骤
i <= sqrt(b)
,请计算b % i
并添加到总和i > sqrt(b)
,请k = floor(b/i)
,然后b % i == b - k*i
和k < sqrt(b)
。因此,对于k = 1
到ceiling(sqrt(b))-1
,请hi = floor(b/k)
和lo = floor(b/(k+1))
。有hi - lo
个数字i
,k*i <= b < (k+1)*i
,b % i
的总和为sum_{ lo < i <= hi } (b - k*i) = (hi - lo)*b - k*(hi-lo)*(hi+lo+1)/2
。如果a <= sqrt(b)
,则只应用第一个项目符号,停在a
。如果sqrt(b) < a < b
在第二个项目符号中从k = floor(b/a)
运行到ceiling(sqrt(b))-1
并将最小k
的上限调整为a
。
总体复杂度O(min(a,sqrt(b)))。
代码(C):
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
unsigned long long usqrt(unsigned long long n);
unsigned long long modSum(unsigned long long a, unsigned long long b);
int main(int argc, char *argv[]){
unsigned long long a, b;
b = (argc > 1) ? strtoull(argv[argc-1],NULL,0) : 10000;
a = (argc > 2) ? strtoull(argv[1],NULL,0) : b;
printf("Sum of moduli %llu %% i for 1 <= i <= %llu: %llu\n",b,a,modSum(a,b));
return EXIT_SUCCESS;
}
unsigned long long usqrt(unsigned long long n){
unsigned long long r = (unsigned long long)sqrt(n);
while(r*r > n) --r;
while(r*(r+2) < n) ++r;
return r;
}
unsigned long long modSum(unsigned long long a, unsigned long long b){
if (a < 2 || b == 0){
return 0;
}
unsigned long long sum = 0, i, l, u, r = usqrt(b);
if (b < a){
sum += (a-b)*b;
}
u = (a < r) ? a : r;
for(i = 2; i <= u; ++i){
sum += b%i;
}
if (r < a){
u = (a < b) ? a : (b-1);
i = b/u;
l = b/(i+1);
do{
sum += (u-l)*b;
sum -= i*(u-l)*(u+l+1)/2;
++i;
u = l;
l = b/(i+1);
}while(u > r);
}
return sum;
}