Time Limit: 5 Sec Memory Limit: 128 MB
Description
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值
其中k mod i表示k除以i的余数。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7
Input
输入仅一行,包含两个整数n, k。
1<=n ,k<=10^9
Output
输出仅一行,即j(n, k)。
Sample Input
5 3
Sample Output
7
简要题解
我们可以把原式数学化一下:求(sum limits_{i=1}^n k mod i)。
我们可以发现
显然,只要(lfloor frac ki
floor)的值在一段段i的范围内是一样的。我们的任务就是要求出每一段这样的范围。我们令(f(x)=lfloor frac kx
floor qquad g(x)=lfloor frac k{lfloor frac kx
floor}
floor),那么其实直觉就可以告诉我们(g(x))就可以表示f值=(lfloor frac kx
floor)的最大的数。然而数学毕竟是一门严谨的科学,我们可能需要来证明一下。
显然(lfloor frac kx
floor leq frac kx),那么(g(x)=lfloor frac k{lfloor frac kx
floor}
floor geq lfloor frac k{frac kx }
floor = x ext{即} g(x) geq x)。所以有(lfloor frac k{g(x)}
floor leq lfloor frac kx
floor)。
同时,(lfloor frac k{g(x)}
floor = lfloor frac k{lfloor frac k{lfloor frac kx
floor}
floor}
floor geq lfloor frac k{ frac k{lfloor frac kx
floor} }
floor = lfloor frac kx
floor),即(lfloor frac k{g(x)}
floor geq lfloor frac kx
floor) 又(lfloor frac k{g(x)}
floor leq lfloor frac kx
floor),所以(lfloor frac k{g(x)}
floor = lfloor frac kx
floor)。
所以(forall i in [x,lfloor frac k{lfloor frac kx
floor}
floor]),(lfloor frac ki
floor)的值都相等!其实之前的猜想的“最大”是很显然的,也没必要再去证一遍了,就算不是最大的,也不影响我们的这个程序。
下面我们就有了一个算法:统计([1,g(1)])的区间里的(lfloor frac ki
floor cdot i)的和,既然(lfloor frac ki
floor)都一样,那就用等差数列求和公式来算一下即可。然后在从(g(1)+1)到(g(g(1)+1))这段区间再如此统计……重复上述步骤,直到(i>k),此时(lfloor frac ki
floor)一定等于0,直接令g(i)=n,统计i..n即可。
下面我们算一下时间复杂度。这个时间复杂度,应该等于(lfloor frac ki
floor)有多少个不同的取值是一样的。当(i leq sqrt k)时,i只有(sqrt k)中取值,所以(lfloor frac ki
floor)也最多只有(sqrt k)种取值。当(i > sqrt k)时,(lfloor frac ki
floor < sqrt k),所以(lfloor frac ki
floor)也最多只有(sqrt k)种取值,所以(lfloor frac ki
floor)一共最多(2 sqrt k)种取值。所以该算法的之间复杂度为(O(sqrt k))。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,k;ll ans;
int main(){
scanf("%d%d",&n,&k);ans=1ll*n*k;
for(register int i=1,g;i<=n;i=g+1){
if(k/i!=0)g=min(n,k/(k/i));else g=n;//错误笔记:如果k/i==0即k<i的话,k/(k/i)会炸掉,所以要特判一下。
ans-=(ll)(k/i)*(i+g)*(g-i+1)/2;
}
printf("%lld
",ans);
}