[BZOJ4176]Lucas的数论
[BZOJ4176]Lucas的数论
试题描述
去年的Lucas非常喜欢数论题,但是一年以后的Lucas却不那么喜欢了。
在整理以前的试题时,发现了这样一道题目“求 \(\sum_{i=1}^n f(i)\)”,其中 \(f(i)\) 表示 \(i\) 的约数个数。他现在长大了,题目也变难了。
求如下表达式的值:
其中 \(f(ij)\) 表示 \(ij\) 的约数个数。
他发现答案有点大,只需要输出模 \(1000000007\) 的值。
输入
第一行一个整数 \(n\)。
输出
一行一个整数 \(ans\),表示答案模 \(1000000007\) 的值。
输入示例
2
输出示例
8
数据规模及约定
\(T \le 10000\)
\(1 \le a,b \le 10^7\)
题解
首先用到个结论
这个东西用归纳法证明,先考虑 \(i\) 和 \(j\) 都只有一种质因子的情况,若该质因子的次数分别为 \(a\) 和 \(b\),那么等号两边显然都是 \(a+b+1\);然后考虑给两个数都加进去一个质因子 \(p'\),它的次数分别为 \(a'\) 和 \(b'\),那么等号左边会乘上 \(a'+b'+1\),等号右边对于所有之前存在的互质数对可以选择在 \(x\) 上乘上 \(p^1, p^2, \cdots , p^{a'}\) 或者在 \(y\) 上乘上 \(p^1, p^2, \cdots, p^{b'}\),还有一种情况就是两边都不乘,所以总共是 \(a'+b'+1\) 种。
那么这题就可以开始大力反演了
\(\mu(d)\) 的前缀和可以很容易杜教筛了,现在就是要快速求 \(\sum_{i=1}^n \lfloor \frac{n}{i} \rfloor\),直接做 \(O(\sqrt n)\),总复杂度是 \(O(n^{\frac{3}{4}})\) 的,考虑用杜教筛优化这个。
现在我们用 \(j\) 替换 \(ij\)
\(f(j)\) 显然可以线性筛,这样就可以预处理前 \(n^{\frac{2}{3}}\) 项了!
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <map>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 1000010
#define MOD 1000000007
#define LL long long
bool vis[maxn];
int prime[maxn], cp, mu[maxn], f[maxn], smu[maxn], sf[maxn], largest[maxn], nos[maxn];
void init() {
int n = maxn - 1;
mu[1] = smu[1] = f[1] = sf[1] = 1; largest[1] = 0;
rep(i, 2, n) {
if(!vis[i]) prime[++cp] = i, mu[i] = -1, f[i] = 2, largest[i] = 1, nos[i] = 1;
for(int j = 1; j <= cp && i * prime[j] <= n; j++) {
vis[i*prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i*prime[j]] = 0;
f[i*prime[j]] = f[nos[i]] * (largest[i/nos[i]] + 2);
largest[i*prime[j]] = max(largest[nos[i]], largest[i/nos[i]] + 1);
nos[i*prime[j]] = nos[i];
break;
}
mu[i*prime[j]] = -mu[i];
f[i*prime[j]] = f[i] << 1;
largest[i*prime[j]] = max(largest[i], 1);
nos[i*prime[j]] = i;
}
smu[i] = smu[i-1] + mu[i];
if(smu[i] >= MOD) smu[i] -= MOD;
if(smu[i] < 0) smu[i] += MOD;
sf[i] = sf[i-1] + f[i];
if(sf[i] >= MOD) sf[i] -= MOD;
}
return ;
}
map <int, int> hSmu, hSf;
int Smu(int n) {
if(n < maxn) return smu[n];
if(hSmu.count(n)) return hSmu[n];
int ans = 1;
for(int i = 2; i <= n; ) {
int r = min(n / (n / i), n);
ans -= (LL)(r - i + 1) * Smu(n / i) % MOD;
if(ans < 0) ans += MOD;
i = r + 1;
}
return hSmu[n] = ans;
}
int Sf(int n) {
if(n < maxn) return sf[n];
if(hSf.count(n)) return hSf[n];
int ans = 0;
for(int i = 1; i <= n; ) {
int r = min(n / (n / i), n);
ans += (LL)(r - i + 1) * (n / i) % MOD;
if(ans >= MOD) ans -= MOD;
i = r + 1;
}
return hSf[n] = ans;
}
LL sqr(int x) { return (LL)x * x % MOD; }
int main() {
init();
int n = read(), ans = 0;
for(int d = 1; d <= n; ) {
int r = min(n / (n / d), n);
ans += (LL)(Smu(r) - Smu(d - 1) + MOD) * sqr(Sf(n / d)) % MOD;
if(ans >= MOD) ans -= MOD;
d = r + 1;
}
printf("%d\n", ans);
return 0;
}
稍微改一改你还可以用它 A 掉 BZOJ3994(注意这题有 \(n, m\),不再取模,且必须开 long long,哦对还有多组数据)。