这个题的题意非常简单,但是比赛时我的大脑一片空白一点思路都没有,无奈结束后看了题解,感叹这个题解法非常巧妙,必须要记录下来。
题意,定义f(i)表示i的所有约数之和,比如f(4)=1+2+4,给你一个区间a,b,让你求这个区间所有的f(i)之和。
思路点拨:我们要求区间a-b的区间和,可以转化为 求1——b的区间和 减去1——(a-1)的区间和,这样比较好算,我们可以写一个函数来计算,所以现在问题转化为计算1——n的区间和,这里我们用一个例子引入一下,比方说现在n等于9,那么1——9的区间和如何计算呢?
我们先来列举一下,这样可以便于发现规律
1:1
2:1 2
3:1 3
4:1 2 4
5:1 5
6:1 2 3 6
7:1 7
8:1 2 4 8
9:1 3 9
所以1——9的区间和等于将上面所有的约数都加起来。
将每个约数出现的次数统计一下,我们可以惊奇的发现
1:9次=(n/1)
2: 4次= (n/2)
3: 3次= (n/3)
........
所以1——9的区间和可以表示为:1*9+2*4+3*3........
到这里我们的任务已经成功一半了。
但是将这些数都一一加起来是肯定是不现实的,一定会tle,因为这个题给了一个很蛋疼的数据:1e12;
所以我们要进行优化,我们会发现有些数出现的次数是相等的,比方说刚才n等于9的例子,我们会发现从5——9他们出现的次数都是1,根据等差数列的公式,我们可以将5+6+7+8+9算出来,然后让结果乘他们出现的次数(这里是1)就可以了,所以我们可以将1——n根据出现的次数相等的将他们划分成一个区间根据等差数列的求和公式计算。
上代码:
#includeusing namespace std; typedef unsigned long long ll; //参考了Damingxkl大佬的代码,这里要开unsinged //long long,否则会爆掉 ll f(ll x) { ll ans=0;//ans记录答案 ll l,r;//左右区间 for(l=1;l<=x;l=r+1)当前的左边界等于上一个区间的右边界加一 { r=x/(x/l); ans=ans+(r-l+1)*(l+r)/2*(x/l);//求和公式 } return ans; } int main() { ll a,b; while(cin>>a>>b) { cout<



