约束和 && 分块

题目背景
Smart最近沉迷于对约数的研究中。

题目描述
对于一个数X,函数f(X)表示X所有约数的和。例如:f(6)=1+2+3+6=12。对于一个X,Smart可以很快的算出f(X)。现在的问题是,给定两个正整数X,Y(X<Y),Smart希望尽快地算出f(X)+f(X+1)+……+f(Y)的值,你能帮助Smart算出这个值吗?

输入格式:
输入文件仅一行,两个正整数X和Y(X<Y),表示需要计算f(X)+f(X+1)+……+f(Y)。

输出格式:
输出只有一行,为f(X)+f(X+1)+……+f(Y)的值。
思路
首先利用前缀和的思想, 我们只要能求出 1−n 的约数和的和, 就能求出一段区间的约数和的和
那么如何快速求出从 1 开始约数和的和呢
很朴素大暴力万岁!的算法是 o n o\sqrt{n} on 计算每个数的约数进行累加 ,然后慢到爆炸

我们可以换种思维, 考虑枚举约数
显然对于一个约数 d , 在 1−n 中出现过 ⌊ n d ⌋ \left\lfloor\frac{n}{d}\right\rfloor dn 次, 所以这一约数贡献的答案为 ⌊ n d ⌋ ∗ d \left\lfloor\frac{n}{d}\right\rfloor * d dnd
所以 1−n 总因数和为 ∑ i = 1 n ⌊ n i ⌋ ∗ i \sum_{i = 1}^{n}\left\lfloor\frac{n}{i}\right\rfloor * i i=1nini

这样依然需要枚举 1−n 这 n 个因数, O(n) 依然达不到复杂度要求

其实看到 ⌊ n d ⌋ \left\lfloor\frac{n}{d}\right\rfloor dn 时我们很兴奋: 除法分块!

(之前学的除法分块是假的。。现在补个真的)
比如说当 n=12 时, 我们分别计算 ⌊ n i ⌋ \left\lfloor\frac{n}{i}\right\rfloor in (1<=i<=n) 可以得到以下结果:12,6,4,3,2,2,1,1,1,1,1,1
注意有部分连续的向下取整值是一样的!除法分块就是 利用打表找规律 来求出每个区间的左右端点以达到进行快速运算的目的
左端点很容易求解, 即为上一个右端点 +1(初始值为1)
右端点通过打表找规律可知: r=n/(n/l)
意思是因子为1的有12个,贡献为12 * 1 = 12,因子为2的有6个,贡献为6 * 2 = 12,因子为3的有4个,贡献为3 * 4 = 12,以此类推。
code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x, y;
ll get_sum(ll n)
{
	ll ans = 0;
	/// l~r之间的所有数都是相同的因子,且每个因子的个数都为n/l
	/// 即为 l*(n/l) + (l+1)*(n/l) + .... r*(n/l); (n/l)提取出来
	for(ll l = 1,r; l <= n; l = r + 1)
    {
		r = n / (n / l);
		ans += (n / l) * (l + r) * (r - l + 1) / 2;
		cout << l << ' ' << r << ' ' << (n / l) * (l + r) * (r - l + 1) / 2 << endl;
		system("pause");
	}
	return ans;
}
int main()
{
	cin >> x >> y;
	cout << get_sum(y) - get_sum(x-1) << endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值