BZOJ传送门
Description
去年的Lucas非常喜欢数论题,但是一年以后的Lucas却不那么喜欢了。
在整理以前的试题时,发现了这样一道题目“求 ∑ f ( i ) \sum f(i) ∑f(i),其中 1 ≤ i ≤ N 1\le i\le N 1≤i≤N”,其中 f ( i ) f(i) f(i)表示 i i i的约数个数。他现在长大了,题目也变难了。
求如下表达式的值:
其中 f ( i j ) f(ij) f(ij)表示 i j ij ij的约数个数。
他发现答案有点大,只需要输出模 1000000007 1000000007 1000000007的值。
Input
第一行一个整数 n n n。
Output
一行一个整数 a n s ans ans,表示答案模 1000000007 1000000007 1000000007的值。
Sample Input
2
Sample Output
8
HINT
对于100%的数据 n ≤ 1 0 9 n \le 10^9 n≤109。
解题分析
首先有个妙妙的结论:
σ
0
(
n
∗
m
)
=
∑
i
∣
n
∑
j
∣
m
[
g
c
d
(
i
,
j
)
=
1
]
\sigma_0(n*m)=\sum_{i|n}\sum_{j|m}[gcd(i,j)=1]
σ0(n∗m)=i∣n∑j∣m∑[gcd(i,j)=1]
设
n
=
p
1
a
1
∗
p
2
a
2
∗
p
3
a
3
∗
.
.
.
.
.
.
∗
p
k
a
k
n=p_1^{a_1}*p_2^{a_2}*p_3^{a_3}*......*p_k^{a_k}
n=p1a1∗p2a2∗p3a3∗......∗pkak,
m
=
p
1
b
1
∗
p
2
b
2
∗
p
3
b
3
∗
.
.
.
.
.
.
∗
p
k
b
k
m=p_1^{b_1}*p_2^{b_2}*p_3^{b_3}*......*p_k^{b_k}
m=p1b1∗p2b2∗p3b3∗......∗pkbk,那么如果
g
c
d
(
i
,
j
)
=
1
gcd(i,j)=1
gcd(i,j)=1, 对于一个质数
p
1
p_1
p1来说, 要么是
i
i
i里含有
p
1
p_1
p1, 共有
a
1
a_1
a1种情况, 要么是
j
j
j里含有
p
1
p_1
p1,共有
b
1
b_1
b1种情况, 要么是两个都没有, 共一种情况, 所以加起来一共是
a
1
+
b
1
+
1
a_1+b_1+1
a1+b1+1种, 刚好符合
σ
0
\sigma_0
σ0的性质。
大力推式子:
∑
i
=
1
n
∑
j
=
1
n
σ
0
(
i
,
j
)
=
∑
i
=
1
n
∑
j
=
1
n
∑
p
∣
i
∑
d
∣
j
[
g
c
d
(
i
,
j
)
=
1
]
=
∑
q
=
1
n
μ
(
q
)
∑
q
∣
p
n
∑
q
∣
d
n
⌊
n
d
⌋
⌊
n
p
⌋
=
∑
q
=
1
n
μ
(
q
)
∑
i
=
1
⌊
n
q
⌋
∑
j
=
1
⌊
n
q
⌋
⌊
n
q
i
⌋
⌊
n
q
j
⌋
\sum_{i=1}^{n}\sum_{j=1}^{n}\sigma_0(i,j) \\ = \sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{p|i}\sum_{d|j}[gcd(i,j)=1] \\ = \sum_{q=1}^n\mu(q)\sum_{q|p}^n\sum_{q|d}^n\lfloor\frac{n}{d}\rfloor\lfloor\frac{n}{p}\rfloor \\ = \sum_{q=1}^n\mu(q)\sum_{i=1}^{\lfloor\frac{n}{q}\rfloor}\sum_{j=1}^{\lfloor\frac{n}{q}\rfloor}\lfloor\frac{n}{qi}\rfloor\lfloor\frac{n}{qj}\rfloor
i=1∑nj=1∑nσ0(i,j)=i=1∑nj=1∑np∣i∑d∣j∑[gcd(i,j)=1]=q=1∑nμ(q)q∣p∑nq∣d∑n⌊dn⌋⌊pn⌋=q=1∑nμ(q)i=1∑⌊qn⌋j=1∑⌊qn⌋⌊qin⌋⌊qjn⌋
设
g
(
x
)
=
∑
i
=
1
x
⌊
x
i
⌋
g(x)=\sum_{i=1}^x\lfloor\frac{x}{i}\rfloor
g(x)=∑i=1x⌊ix⌋,然后发现
g
(
x
)
=
σ
0
(
x
)
g(x)=\sigma_0(x)
g(x)=σ0(x) ,就可以把上面这玩意化成:
∑
q
=
1
n
μ
(
q
)
σ
0
2
(
⌊
n
q
⌋
)
\sum_{q=1}^n\mu(q)\sigma_0^2(\lfloor\frac{n}{q}\rfloor)
q=1∑nμ(q)σ02(⌊qn⌋)
∑
i
=
1
n
μ
(
i
)
\sum_{i=1}^n\mu(i)
∑i=1nμ(i)我们已经可以用杜教筛筛出来了, 现在考虑后面那个玩意。由于我们知道
I
(
n
)
∗
I
(
n
)
=
σ
0
(
n
)
I(n)*I(n)=\sigma_0(n)
I(n)∗I(n)=σ0(n), 那么莫比乌斯反演一下即可得到
σ
0
(
n
)
∗
μ
(
n
)
=
I
(
n
)
\sigma_0(n)*\mu(n)=I(n)
σ0(n)∗μ(n)=I(n)。 这样就可以科学筛出
σ
0
\sigma_0
σ0前缀和了。
代码如下:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#include <tr1/unordered_map>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define ll long long
#define MOD 1000000007ll
#define MX 5000050
std::tr1::unordered_map <int, int> mp1;
std::tr1::unordered_map <int, ll> mp2;
int pcnt, n;
int pri[MX], miu[MX], sig0[MX], sum1[MX], cnt[MX];
bool npr[MX];
ll sum2[MX];
void get()
{
miu[1] = 1, sig0[1] = 1;
R int i, j, tar;
for (i = 2; i <= 5e6; ++i)
{
if (!npr[i]) miu[i] = -1, sig0[i] = 2, cnt[i] = 1, pri[++pcnt] = i;
for (j = 1; j <= pcnt; ++j)
{
tar = i * pri[j];
if (tar > 5e6) break;
npr[tar] = true;
if (!(i % pri[j]))
{
miu[tar] = 0;
sig0[tar] = sig0[i] / (cnt[i] + 1) * (cnt[i] + 2);
cnt[tar] = cnt[i] + 1;
break;
}
miu[tar] = -miu[i];
sig0[tar] = 2 * sig0[i];
cnt[tar] = 1;
}
}
for (R int i = 1; i <= 5e6; ++i)
sum1[i] = sum1[i - 1] + miu[i], sum2[i] = (sum2[i - 1] + sig0[i]) % MOD;
}
ll solve1(R int pos)//for miu
{
if (pos <= 5e6) return sum1[pos];
if (mp1.count(pos)) return mp1[pos];
ll ret = 1;
for (R int lef = 2, rig; lef <= pos; lef = rig + 1)
{
rig = pos / (pos / lef);
ret -= (rig - lef + 1) * solve1(pos / lef) % MOD;
ret %= MOD;
}
return mp1[pos] = ret;
}
ll solve2(R int pos)
{
if (pos <= 5e6) return sum2[pos];
if (mp2.count(pos)) return mp2[pos];
ll ret = pos;
for (R int lef = 2, rig; lef <= pos; lef = rig + 1)
{
rig = pos / (pos / lef);
ret -= (solve1(rig) - solve1(lef - 1)) * solve2(pos / lef) % MOD;
ret %= MOD;
}
return mp2[pos] = ret;
}
int main(void)
{
ll ans = 0;
scanf("%d", &n); get();
for (R int lef = 1, rig; lef <= n; lef = rig + 1)
{
rig = n / (n / lef);
ans = (ans + (solve1(rig) - solve1(lef - 1)) * solve2(n / lef) % MOD * solve2(n / lef) % MOD) % MOD;
}
printf("%lld", (ans + MOD) % MOD);
}