做题总结——Fear Factoring
原题
题意分析:
这道题目题意很简单,就是求出[a,b]区间内所有数的因数之和
做题思路:
开始自己的做法是特别愚蠢的 暴力破解法,也就是分别枚举[a,b]区间里每一个数的因数,最后累加起来,可想而知这种做法肯定是不对的。后面听了学长的讲解,这道题一共有两种思路。
- 枚举从1到sqrt(b)的每一个数i,求出[a,b]能够以i作为因数的最小的那个数,这样就可以得到[a,b]区间内所有可以以i为因数的数,再进行累加即可
- 利用数论分块的方法,具体分析参考数论分块解决
代码实现
1、暴力破解法:
//利用暴力破解法
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long a, b, i;
while (cin >> a >> b)
{
long long ans=0;
for (i = 1; i * i <= b; i++)
{
long long d = (a / i) * i; //求出[a,b]内第最小的能够以i作为因数的数
while (d < a || i*i>d) //如果求出的d小于a或者原来已经求过因数了,取下一个能够以i为因数的(也就是d+i)
{
d+=i;
}
for (d; d <= b; d += i) //累加因数
{
ans += i;
if (i < d / i) //这里的判断防止累加重复了
{
ans += d / i;
}
}
}
cout << ans << endl;
}
//system("pause");
return 0;
}
2、数论分块法
//利用数论分块的思想解决该问题
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll; //这里不用unsigned long long无法AC
ll sol(ll n)
{
ll ans=0;
ll r;
for(ll l=1;l<=n;l=r+1) //枚举因数,同时更新区间的右端点
{
ll s=n/l; //s是从1到n中含因数l的数的个数
r=n/s; //[l,r]中的每一个数能作为从1到n中s个数的因数(相当于求分块区间的右端点)(这里描述不太准确,以后想明白再修改)
ans+=(l+r)*(r-l+1)/2*s; //[l,r]作为因数的贡献,相当于求一个等比数列的值
}
return ans;
}
int main()
{
ll a,b;
while((scanf("%llu%llu", &a, &b)!=EOF))
{
cout<<sol(b)-sol(a-1)<<endl;
}
//system("pause");
return 0;
}