时空限制
1s / 128MB
题目背景
Smart最近沉迷于对约数的研究中。
题目描述
对于一个数X,函数
f(X)
表示
X
所有约数的和。例如:
输入输出格式
输入格式:
输入文件仅一行,两个正整数
X
和
输出格式:
输出只有一行,为 f(X)+f(X+1)+……+f(Y) 的值。
输入输出样例
输入样例#1:
2 4
输出样例#1:
14
输入样例#2:
123 321
输出样例#2:
72543
说明
对于 20% 的数据有 1≤X<Y≤105 。
对于 60% 的数据有 1≤X<Y≤1∗107 。
对于 100% 的数据有 1≤X<Y≤2∗109 。
solution
把 f(x) 用数学方式表示一下就是 f(x)=∑d|xd
那 ans=∑i=xyf(i)=∑i=xy∑d|id
那我们就可以直接枚举然后累加就可以了。
但这样的时间复杂度是 O(∑i=xyi√) ,会 TLE
所以换一种思路,枚举约数
考虑 1−n 中有几个数是 d 的倍数
假如
1−n 中存在 d 的倍数,那这个数肯定可以表示为k⋅d(k∈N+) k 的范围可以再简化一下,
1≤k⋅d≤n⇒1≤k≤⌊nd⌋ 也就是说从 1−n 中把 d 的倍数单独拿出来,那就是
d,2d,3d.....⌊nd⌋d 所以 1−n 中 d 的倍数的个数就是
⌊nd⌋ 求出 y 的个数,再减去
x−1 的个数,也就是 x−y 的个数,这个是比较好想的,所以我就不详细说了。这样 ∑i=1nf(i) 就可以表示为 ∑i=1n(⌊ni⌋∗i)
那 ans=∑i=1y(⌊yi⌋∗i)−∑i=1x−1(⌊x−1i⌋∗i)
这种做法时间复杂度是 O(y) ,还是会 TLE
再看 ∑i=1n(⌊ni⌋∗i)
只看 ⌊ni⌋ ,胡乱找个数列出来 ⌊ni⌋(1≤i≤n) 的值
以 12 为例,列出来是 12,6,4,3,2,2,1,1,1,1,1,1 ,第 i 个数表示
⌊ni⌋ 的值发现这里面有些数是重复的,考虑能不能把这些重复的一次算出来
把那些相同的值用区间来表示,那只要求出左右端点 l,r 来就好了
l 比较好求,观察上面的数列,
l 就是上一个 r 加1 ,初始 l=1那 r 怎么求呢?
其实很简单r=n/(n/l) l 是那个数列的下标,所以
(n/l) 就是约数,那 r 就显然了,如果不知道为什么,那就再看一遍“1−n 中有几个数是 d 的倍数”。l 和 r 都知道了, 那答案呢?ans+= 约数*约数的个数- 约数 =n/l
约数的个数 =∑i=lri ,用等差数列求和公式表示一下就是 (l+r)∗(r−l+1)/2
即 ans+=(n/l)∗(l+r)∗(r−l+1)/2
然后就愉快的AC啦!
code
比题解不知道短到那里去的代码
#include<cstdio>
using namespace std;
typedef long long ll;
ll sum(int n) {
if(n<=1) return n;
ll ans=0;
for(ll l=1,r;l<=n;l=r+1) {
r=n/(n/l);
ans+=(n/l)*(l+r)*(r-l+1)/2;
}
return ans;
}
int main() {
int x,y;
scanf("%d%d",&x,&y);
printf("%lld\n",sum(y)-sum(x-1));
return 0;
}