[Luogu 4176] Lucas的数论

BZOJ传送门

Description

去年的Lucas非常喜欢数论题,但是一年以后的Lucas却不那么喜欢了。

在整理以前的试题时,发现了这样一道题目“求 ∑ f ( i ) \sum f(i) f(i),其中 1 ≤ i ≤ N 1\le i\le N 1iN”,其中 f ( i ) f(i) f(i)表示 i i i的约数个数。他现在长大了,题目也变难了。

求如下表达式的值:

img

其中 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 n109

解题分析

首先有个妙妙的结论:
σ 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(nm)=injm[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=p1a1p2a2p3a3......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=p1b1p2b2p3b3......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=1nj=1nσ0(i,j)=i=1nj=1npidj[gcd(i,j)=1]=q=1nμ(q)qpnqdndnpn=q=1nμ(q)i=1qnj=1qnqinqjn
g ( x ) = ∑ i = 1 x ⌊ x i ⌋ g(x)=\sum_{i=1}^x\lfloor\frac{x}{i}\rfloor g(x)=i=1xix,然后发现 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=1nμ(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);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值