数论分块:(\lfloor \frac n i \rfloor)最多只有(2 · \lfloor \sqrt n \rfloor)个取值。
for(ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= (r - l + 1) * S(n / l);
}
常见的积性函数:
φ(n) -欧拉函数
μ(n) -莫比乌斯函数,关于非平方数的质因子数目
gcd(n,k) -最大公因子,当k固定的情况
d(n) -n的正因子数目
σ(n) -n的所有正因子之和
1(n) -不变的函数,定义为 1(n) = 1 (完全积性)
Id(n) -单位函数,定义为 Id(n) = n(完全积性)
Idk(n) -幂函数,对于任何复数、实数k,定义为Idk(n) = n^k (完全积性)
ε(n) -定义为:若n = 1,ε(n)=1;若 n > 1,ε(n)=0。别称为“对于狄利克雷卷积的乘法单位”(完全积性)
欧拉函数:
通俗定义:欧拉函数是小于n的正整数中与n互质的数的数目(φ(1)=1)
重要性质:
∑
d
∣
n
ϕ
(
d
)
=
n
\sum_{d|n}\phi(d) = n
d∣n∑ϕ(d)=n
欧拉函数表:1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10, 22, 8, 20, 12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22, 46, 16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20, 66, 32, 44
(O(\sqrt n))求欧拉函数:
ll euler(ll x)
{
ll res = x;
for(ll i = 2; i * i <= x; ++i){
if(x % i == 0){
res = res / i * (i - 1);
while(x % i == 0) x /= i;
}
}
if(x > 1) res = res / x * (x - 1);
return res;
}
莫比乌斯函数:
如果一个数包含平方因子,那么miu(n) = 0。例如:miu(4), miu(12), miu(18) = 0
如果一个数不包含平方因子,并且有k个不同的质因子,那么miu(n) = (-1)^k
通俗定义:μ(1) = 1。若i有平方因子,则μ(i) = 0。否则μ(i) = (-1)^k,k为i的质因数分解中质数的个数。
重要性质:
∑
d
∣
n
μ
(
d
)
=
[
n
=
1
]
\sum_{d|n}\mu(d) = [n = 1]
d∣n∑μ(d)=[n=1]
莫比乌斯函数表: 1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1, 0, 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, -1, -1, 0, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, 1, 0, 1, 1, -1, 0, -1, 1, 0, 0, 1, -1, -1, 0, 1, -1, -1, 0, -1, 1, 0, 0, 1, -1
(O(\sqrt n))求莫比乌斯函数:
int mobius(ll x)
{
int cnt = 0;
for (ll i = 2; i * i <= x; ++i){
if (x % i == 0) ++cnt, x /= i;
if (x % i == 0) return 0;
}
if (x != 1) ++cnt;
return cnt & 1 ? -1 : 1;
}
迪利克雷卷积(Dirichlet product):
(
f
∗
g
)
(
i
)
=
∑
d
∣
n
f
(
d
)
⋅
g
(
n
d
)
(f * g)(i) = \sum_{d|n}f(d)·g(\frac n d)
(f∗g)(i)=d∣n∑f(d)⋅g(dn)
迪利克雷卷积推杜教筛:https://www.luogu.org/problemnew/solution/P4213
杜教筛:
const int maxn = 2e6 + 5;
ll prime[maxn], mu[maxn], phi[maxn], sieve[maxn], mem[maxn], cons;
//线性筛
void init()
{
prime[1] = phi[1] = mu[1] = 1;
int cnt = 0;
for (int i = 2; i < maxn; ++i){
if (!prime[i]) phi[i] = i - 1, mu[i] = -1, sieve[++cnt] = i;
for (int j = 1; j <= cnt && i * sieve[j] < maxn; ++j){
prime[i * sieve[j]] = 1;
if (i % sieve[j] == 0){
phi[i * sieve[j]] = phi[i] * sieve[j];
mu[i * sieve[j]] = 0;
break;
}else{
phi[i * sieve[j]] = phi[i] * (sieve[j] - 1);
mu[i * sieve[j]] = -mu[i];
}
}
}
for (int i = 2; i < maxn; ++i){
phi[i] += phi[i - 1];
phi[i] %= mod;
mu[i] += mu[i - 1];
}
return ;
}
//杜教筛
inline ll S(ll n)
{
if (n <= maxn) return phi[n];
if (mem[cons / n] != INF) return mem[cons / n];
//这里是求欧拉函数和,求莫比乌斯函数和把res初始化成1就好了。
ll res = (n % mod) * ((n + 1) % mod) / 2 % mod;
for(ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= (r - l + 1) * S(n / l);
}
res %= mod; res += mod; res %= mod;
return mem[cons / n] = res;
}
ll cal(ll x)
{
cons = x;
memset(mem, 0x3f, sizeof(mem));
return S(x);
}
int main()
{
init();
ll a;
scanf("%lld",&a);
printf("%lld\n",cal(a));
return 0;
}
杜教筛的另一种写法,用map,多个log,但是不用清零,多组数据的时候更快。
http://acm.hdu.edu.cn/showproblem.php?pid=6706
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7, inf = 0x3f3f3f3f;
ll quickpow(ll x, ll k)
{
ll res = 1;
while(k){
if(k & 1) res = (res * x) % mod;
k >>= 1, x = (x * x) % mod;
}
return res;
}
const int maxn = 2e6;
unordered_map<ll,ll> mii;
ll inv2, inv6;
ll sum1(ll l, ll r)
{
return (l + r) * (r - l + 1) / 2 % mod;
}
ll sum2(ll x)
{
return x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod;
}
ll prime[maxn + 10], phi[maxn + 10], sieve[maxn + 10], mem[maxn + 10];
//线性筛
void init()
{
prime[1] = phi[1] = 1;
int cnt = 0;
for (int i = 2; i <= maxn; ++i){
if (!prime[i]) phi[i] = i - 1, sieve[++cnt] = i;
for (int j = 1; j <= cnt && i * sieve[j] <= maxn; ++j){
prime[i * sieve[j]] = 1;
if (i % sieve[j] == 0){
phi[i * sieve[j]] = phi[i] * sieve[j];
break;
}else{
phi[i * sieve[j]] = phi[i] * (sieve[j] - 1);
}
}
}
for (int i = 2; i <= maxn; ++i){
phi[i] = i * phi[i] + phi[i - 1];
phi[i] %= mod;
}
}
//杜教筛,筛i * phi(i)前缀和
ll S(ll n)
{
if (n <= maxn) return phi[n];
if (mii[n]) return mii[n];
ll res = sum2(n);
for(int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= sum1(l, r) * S(n / l) % mod;
res %= mod;
if (res < 0) res += mod;
}
return mii[n] = res;
}
int main()
{
init();
inv2 = quickpow(2, mod - 2);
inv6 = quickpow(6, mod - 2);
int t;
scanf("%d", &t);
while (t--){
ll n, a, b;
scanf("%lld %lld %lld", &n, &a, &b);
ll ans = S(n);
--ans;
printf("%lld\n", ans * inv2 % mod);
}
return 0;
}
莫比乌斯反演:先鸽一下以后遇到这种题再写
[
g
c
d
(
i
,
j
)
=
1
]
⇔
∑
d
∣
g
c
d
(
i
,
j
)
μ
(
d
)
[gcd(i, j) = 1] \Leftrightarrow \sum_{d | gcd(i, j)} \mu(d)
[gcd(i,j)=1]⇔d∣gcd(i,j)∑μ(d)