题目描述
给定一个\(n\),求\(\displaystyle \sum_{i=1}^n\sum_{p|i}d(p)\),d(n)表示n的约数个数。
\(n \le 10^{11}\)
方法一:
原式等价于\(\displaystyle \sum_{i=1}^{n}\lfloor \frac{n}{i} \rfloor d(i)\)
线性筛即可
代码就不放了。
方法二:
对于上面的式子\(\displaystyle \sum_{i=1}^{n}\lfloor \frac{n}{i} \rfloor d(i)\)我们可以数论分块,问题就变成了如何快速的求约数个数的前缀和
能1s跑\(10^{10}\)不成问题。但考试的时候没有这部分分。
方法三:
原题等价于求 \(xyz \le n\) 的有序三元组个数.
原题等价于求\(\displaystyle \sum_{i=1}^{n}\sum_{p|i}\sum_{d|p}1\),把i拆两次能得到3个数。
假设 \(x \le y \le z\),则 \(x \le n ^{\frac{1}{3}}\) ,\(\displaystyle y \le \sqrt \frac{n}{x}\)
枚举 x, y,我们可以计算出合法的 z 的数量.
假设合法数为 ans,则 x, y, z 可以任意排列,因此 ans ∗ 6.
但是相等情况会重复计数,我们再计算有两个数相等、三个数全相等的情况数.
两个数相等\(\displaystyle \sum \frac{n}{i^2}\),三个数全相等:\(n ^{\frac{1}{3}}\)
容斥一下就行啦。
代码出奇的短.
#include<iostream>
#include<cmath>
#define LL long long
using namespace std;
LL n,ans;
int main()
{
cin>>n;
LL toi=floor(pow(n,0.34));while(toi*toi*toi>n)--toi;
for(LL i=1;i<=toi;++i)
for(LL j=i,toj=sqrt(n/i);j<=toj;++j)ans+=n/(i*j)-j+1;
ans*=6;
for(LL i=1;i*i<=n;++i)ans-=(n/i/i)*3;
ans-=2*toi;
cout<<ans;
return 0;
}