toj2861_Divisors_线性时间内计算n前约数的总和

思想: 数形结合,求在双曲线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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值