思想: 数形结合,求在双曲线x*y = n在第一象限分支中下方的整点的个数。
作直线x=y,于是可以先计算上半部分(含x=y这条直线)的点数。
x=1的时候有m个,x=2的时候有m/2-1个。。。
于是乘以2。
然后x=y这条直线上的i-1个点多计算了一次,于是要减去(i-1)个。
/**********************************我的理解*******************************************/
因为该曲线下方的任一整点(x,y)均满足x*y<=n,不妨x*y=m<=n,那么显然x,y就都是m的约数了,所以每多一个整点就意味着1~n的约数多了两个,但是又
因为如果x*y=m<=n被计算为两个约数,那y*x也一定会被计算两次,这就重复了,所以对于每个整点,我们只加一。这样一来,求1~n的所有约数的个数和
就等价于求x*y=n这曲线下方所有整点个数的和了。
/**************************************************************************************/
long long int a[10]={0,1,3,5,8,10};
long long int f(long long int m)
{
if (m<=5) return a[m];
long long sum = 0;
long long int i;
for (i = 1; i*i <= m; ++i)
{
sum += m/i - (i - 1);
}
return sum*2-i+1;
}
当然toj这题其实就是求这个f(b)-f(a-1),这点很好理解。转换一下就好了。
贴上我鲜嫩多汁刚AC的代码:
#include <cstdio>
typedef long long ll;
ll tmp[10] = {0, 1, 3, 6, 9};
/* x*y=a下方整点个数 */
ll sum(ll a) {
if (a < 5) return tmp[a];//这里只是个人习惯,单纯为了省事,把小数据(a=0等)极端情况直接手算出来。。。按照这里的算法,其实没有必要,算法对0,1等也不需要特判
ll res = 0LL, i = 1LL; //给long long类型加LL后缀只是个人习惯,,,
for (; i*i <= a; ++i) {
res += a/i - (i - 1);
}
return res*2 - (i-1);
}
int main() {
ll a, b;
while (~scanf(" %lld %lld", &a, &b)) {
if (a == 0LL && b == 0LL) break;
printf("%lld\n", sum(b)-sum(a-1));
}
return 0;
}