因数平方和

该博客探讨了因数平方和的问题,即求解f(x)的和g(n)模10^9+7的余数,其中f(x)表示x的所有因数平方的和。博主提出了利用数论分块和逆元的概念,将时间复杂度优化到O(√n),并提供了解题思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因数平分和

1.题目描述

记 f ( x ) f(x)f(x) 为 x xx 的所有因数的平方的和。例如: f ( 12 ) = 1 2 + 2 2 + 3 2 + 4 2 + 6 2 + 1 2 2 f(12)=1^{2}+2^{2}+3^{2}+4^{2}+6^{2}+12^2f(12)=1 2+2 2+3 2+4 2+6 2+12 2定义 g ( n ) = ∑ i = 1 n f ( i ) g(n)=\sum_{i=1}^{n} f(i)g(n)=∑ i=1n f(i) 。给定 n nn, 求 g ( n ) g(n)g(n) 除以 1 0 9 + 7 10^{9}+710 9+7 的余数。

2.输入格式

输入一行包含一个正整数 nn 。

3.输出格式

输出一个整数表示答案 g ( n ) g(n)g(n) 除以 1 0 9 + 7 10^{9}+710 9+7 的余数。

4.样例输入

100000

5.样例输出

680584257

6.数据范围

1 ≤ n ≤ 1 0 9 1\leq n \leq 10^{9}1≤n≤10

9

2.解题思路

根据定义可知 g ( n ) = f ( 1 ) + f ( 2 ) + f ( 3 ) + . . . + f ( n ) g(n)=f(1)+f(2)+f(3)+...+f(n)g(n)=f(1)+f(2)+f(3)+...+f(n)。对于任意的 f ( i ) f(i)f(i),其值为它所有的因数的平方和累加, 反过来想,对于任意一个数 i ii ,在[ 1 , n ] [1,n][1,n]中有多少个数是 i ii 的倍数,则 i 2 i^2i 2 则会被累加几次。

根据倍数的原理我们可知:

如果按照上述式子计算的话,时间复杂度为O ( n ) O(n)O(n),而 n nn 最大为1e9,说明我们需要优化。仔细观察式子 ⌊ n i ⌋ \lfloor \frac{n}{i} \rfloor⌊ in⌋ 是一个模板的数论分块的式子,它可以帮助我们将求和的复杂度降低到O ( n ) O(\sqrt n)O( n)。简略而言就是我们可以将区间[ 1 , n ] [1,n][1,n]分成若干块区间 [ l , r ] [l,r][l,r],使得满足 ⌊ n l ⌋ = ⌊ n l + 1 ⌋ = ⌊ n l + 2 ⌋ = ⋯ ⋯ = ⌊ n r ⌋ \lfloor \frac{n}{l} \rfloor=\lfloor\frac{n}{l+1} \rfloor=\lfloor\frac{n}{l+2}\rfloor=⋯ \cdots=\lfloor \frac{n}{r}\rfloor⌊n⌋=⌊ l+1n⌋=⌊ l+2n⌋=⋯⋯=⌊rn⌋

如果要求任意区间的[ l , r ] [l,r][l,r]的平方和,那么只要类似前缀和思想用 s [ r ] − s [ l − 1 ] s[r]-s[l-1]s[r]−s[l−1]即可。在计算答案时,我们只需要保留记录一下[ 1 , l − 1 ] [1,l-1][1,l−1]的平方前缀和就好,代码中用ans变量记录。再通过公式计算[ 1 , r ] [1,r][1,r]的平方前缀和。这里需要注意的是公式中涉及了除法,而且题目还需要进行取模,所以我们需要使用逆元,否则会出现精度问题,这里先提前计算出了6在1e9+7下的逆元为166666668。不了解逆元的请自行学习。

至此,我们可以不断累积计算每个块状区间的答案,时间复杂度为O ( n ) O( \sqrt n)O( n)。具体实现见代码细节

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> PII;
#define pb(s) push_back(s);
#define SZ(s) ((int)s.size());
#define ms(s,x) memset(s, x, sizeof(s))
#define all(s) s.begin(),s.end()
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 200010;
LL n;
LL ans = 0, sum = 0;
// 储存结果
LL res = 0;
int inv6 = 166666668; //6在1e9+7下的逆元
//数论分块
void H() {
LL l = 1, r; // 块左端点与右端点
while (l <= n) {
r = n / (n / l); // 计算当前块的右端点
ans = sum;
sum = r * (r + 1) % mod * (2 * r + 1) % mod * inv6 % mod;
LL v = (sum - ans + mod) % mod;//求出区间[l,r]的平方和
res = (res + (n / l) % mod * v % mod) % mod;
l = r + 1; // 左端点移到下一块
}
}
void solve()
{
cin >> n;
H();
cout << res << '\n';
}
int main()
{
ios_base :: sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while (t--)
{
solve();
}
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值