时空限制
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;
}