数论2&莫&杜
积性函数:
积性函数定义ok
积性函数指对于所有互质的整数\(a\)和\(b\)有性质\(f(ab)=f(a)f(b)\)的数论函数
除数函数?
莫比乌斯函数\(\mu\)ok
为什么是积性函数?
对任意\(a,b,gcd(a,b)=1\), \(ab\)的质因子集合可以不重不漏分到\(a,b\)的质因子集合中
分类讨论一下,满足定义
欧拉函数\(\phi\)ok
小于等于\(n\)的数中与\(n\)互质的数的数目
通式:
通式的意义:把他展开, 相当于做容斥, 筛除的所有\(n\)因数
为什么是积性函数?
对任意\(a,b,gcd(a,b)=1\), \(a,b\)没有不含有相同的\(p\), 直接带入通式得证
反正关于欧拉函数的东西盯着这个通式就好了
幂函数\(Id\)ok
\(id_k(n)=n^k\)
\(id(n)=id_1(n)=n\)
显然是积性函数
单位函数\(\epsilon\)ok
\(\epsilon(n)=[n=1]\)
显然是积性函数
线性筛
核心:保证每个数只被他最小的质数筛到一次
int tot;
bool vis[MAXN];
int p[MAXN], phi[MAXN], mu[MAXN];
void filter()
{
phi[1] = mu[1] = 1;
for (int i = 2; i <= n; ++ i)
{
if (!vis[i]) p[++ tot] = i, phi[i] = i - 1, mu[i] = -1;
for (int j = 1; j <= tot && i * p[j] <= n; ++ j)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{
phi[i * p[j]] = phi[i] * p[j]; mu[i * p[j]] = 0;
//没有增加质因子, 结合phi的通式可得
break;
}
else
{
phi[i * p[j]] = phi[i] * phi[p[j]];
mu[i * p[j]] = -mu[i];
}
}
printf("%d %d\n", mu[i], phi[i]);
}
}
Dirichlet卷积
交换律 \(f ∗ g = g ∗ f\)
结合律 \((f ∗ g) ∗ h = f ∗ (g ∗ h)\)
分配律 \(f ∗ (g + h) = f ∗ g + f ∗ h\)
单位元 \(f ∗ ϵ = f\)
若 \(f,g\) 均为积性函数,则 \(f*g\) 也是积性函数
证明:
对任意\(a,b,gcd(a,b)=1\), \(ab\)的质因子集合可以不重不漏分到\(a,b\)的质因子集合中
\((f*g)(ab) = \sum_{d|ab}f(d)g(\frac{ab}{d})\)
设\(d=d_a*d_b\), \(d_a,d_b\)分别由\(a,b\)的因子组成, 显然组成的方式唯一, 并且\(d\)唯一分解成\(d_a,d_b\)
已知\(f,g\),\(O(nlog_2^n)\)求\((f*g)\)
for (int i = 1; i * i <= n; ++ i)
{
(f*g)[i*j] += f[i] * g[i];
for (int j = i + 1; i * j <= n; ++ j)
(f*g)[i*j] += f[i] * g[j] + f[j] * g[i];
}
不会证
常见数论函数
\(\sigma(n)\)表示整数\(n\)的所有正因数的和
\(\tau(n)\)表示整数\(n\)的所有正因数的个数
\(Id(i)=i\)
以及前面的一些积性函数
常见狄利克雷卷积及其证明
\(\epsilon = \mu *1\)
证明:
\(n\)含有多个相同质因子或\(=1\)是显然成立
考虑\(n=p_1*p_2*...*p_k\), \(p_i\)都是不同质因子
由于\((x+y)^k=\sum x^iy^{k-i}*\binom{k}{i}\), 带入\(x=-1, y=1\), 凑进去
\(\sigma = Id*1\)
\(\tau=1*1\)
显然
\(\phi = \mu * Id\)
证明:
\(\phi * 1= Id\)
上面一个等式两边同乘以\(1\)
莫比乌斯Van演
定理:\(\epsilon = \mu * 1\)
若函数 \(f, g\) 满足
\(f(n) = \sum_{d|n}g(d)\)
则
\(g(n) = \sum_{d|n}\mu(d)f(\frac{n}{d}) = \sum_{d|n}\mu(\frac{n}{d})f(d)\)
证明:$
\(f = g*1 \longleftrightarrow \mu * f = g\) $
左式同乘以\(\mu\), 右式同乘以\(1\)
常用:
运用这个卷积:
\(\epsilon = \mu * 1\) 即 \(\sum_{d|n}\mu(d) = 1\)
\([\gcd(i, j) == 1] = \sum_{k | \gcd(i,j)} \mu(k)\)
例题:
NOI2015寿司
BZOJ1257: CQOI2007余数之和
题意:
\(n, k(\le10^9)\), 求\(\sum_{i=1}^{n}(k \mod i)\)
Sol:
\(\sum_{i=1}^{n}(k \mod i) = \sum_{i=1}^{n}(k - k / i * i) = nk - \sum_{i=1}^{n}(k/i)*i\)
BZOJ1101: POI2007Zap
\(T\) 组询问,\(1 ≤ T, n, m , d≤ 50000\)
相当于求
n /= d, m /= d
根据\(\epsilon = \mu * 1\),即求
除法分块\(O(\sqrt{n})\)
BZOJ2154: Crash的数字表格
\(1\le n, m\le10^7\), 求
\(i*j=\gcd(i,j) * lcm(i,j)\)
设\(p=g*d\)
完了,然后怎么做?
大爷xyyxyyx说: \(F(p)=\sum_{d|p}d*\mu(d)*p\)是积性函数
为什么?
设\(a,b,gcd(a,b)=1,d=da*db,da|a,db|b\)
好了, 然后\(O(n)\)筛?然后暴力\(O(n)\)求答案?
观察\(F(i)\)的式子, 套用线性筛:
- \(i\)为质数: \(F(i)=(1*\mu(1)+i*\mu(i))*i\)
- 用已经筛出来的质数\(p[j]\)去筛\(i*p[j]\)
- \(i\mod p[j]!=0\), 互质!\(F(i*p[j])=F(i)*F(p[j])\)
- \(i \mod p[j] == 0\),考虑对比\(F(i)\),新加入的因数必须含有\(p^2\), 而这样的因数\(\mu{}\)是等于0的!那么直接\(F(i*p[j])=F(i)*p[j]\)
然后少写了一个\(mod\), WA了,然后又T了
整出分块!!注意到在一段区间里\((n/p, m/p)\)是不变的, 所以可以对\(F\)直接区间求和(前缀和实现)
//空间注意一点
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1e7 + 10;
const LL MOD = 20101009;
int n, m;
LL ans;
int tot;
bool vis[MAXN];
int mu[MAXN], p[664590];
LL f[MAXN];
int sum[MAXN], sumf[MAXN];
void filter()
{
mu[1] = sumf[1] = f[1] = sum[1] = 1;
LL tmp1 = 0;
for (int i = 2; i <= m; ++ i)
{
tmp1 = 1LL * i * (i + 1) / 2 % MOD;
sum[i] = tmp1;
if (!vis[i]) mu[i] = -1, p[++ tot] = i, f[i] = 1LL * (1 + i * mu[i] % MOD + MOD) * i % MOD;
for (int j = 1; j <= tot && i * p[j] <= m; ++ j)
{
vis[i * p[j]] = true;
if (i % p[j] == 0) { mu[i * p[j]] = 0; f[i * p[j]] = f[i] * p[j] % MOD; break; }
else mu[i * p[j]] = -mu[i], f[i * p[j]] = f[i] * f[p[j]] % MOD;
}
sumf[i] = (sumf[i - 1] + f[i]) % MOD;
}
}
int main()
{
scanf("%d%d", &n, &m);
if (n > m) swap(n, m);
filter();
for (int i = 1; i <= n; )
{
int r = min(n, min(n / (n / i), m / (m / i)));
(ans += sum[n / i] % MOD * sum[m / i] % MOD * (sumf[r] - sumf[i - 1] + MOD) % MOD) %= MOD;
i = r + 1;
}
printf("%lld\n", ans);
return 0;
}
PE530: GCD of Divisors
Let f(n) be the sum of the greatest common divisor of d and n/d over all positive divisors d of n, that is \(f(n)=\displaystyle\sum\limits_{d|n}\, \text{gcd}(d,\frac n d)\)
Let F be the summatory function of f, that is \(F(k)=\displaystyle\sum\limits_{n=1}^k \, f(n)\)
You are given that \(F(10)=32\) and \(F(1000)=12776.\)
Find \(F(1015)\).
提交答案题
相当于枚举\(i\), 并将\(i\)分解成两个数的乘积, 求所有这样的\(\gcd\)的和
等价于枚举两个数, 他们乘积\(\le n\), 求他们\(\gcd\)的乘积
所以:
注意不能像以前那样把两个\(\sum{}\)的上界除以\(g\)
从而认为\(i*j\le n/g\)
因为两个\(\sum{}\)是有关联的,只有当第一个确定时,才能枚举第二个, 如果这么做的话相当于同时枚举两个
例如\(i*j\le n*n/i\)显然不对
那么继续
考虑把\(g*d\)并在一起, 然后康康这个和什么卷积比较像
于是就化简成了
什么,要求\(10^{15}\)?不会炸空间吗?
注意后面\(\lfloor \frac{n}{p^2}\rfloor\), 可以缩小到\(\sqrt{n}\)
那么枚举\(p\), 然后每次单独分块求就好了
复杂度呢?前半个\(O(\sqrt{n})\), 后半个\(O(ln_n)\)?不会证...
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 32e6 + 10;
const LL MOD = 20101009;
LL n;
LL ans;
int tot;
bool vis[MAXN];
LL p[MAXN], phi[MAXN];
LL f[MAXN];
void filter()
{
phi[1] = 1;
for (int i = 2; i < MAXN; ++ i)
{
if (!vis[i]) p[++ tot] = i, phi[i] = i - 1;
for (int j = 1; j <= tot && i * p[j] < MAXN; ++ j)
{
vis[i * p[j]] = true;
if (i % p[j] == 0) { phi[i * p[j]] = phi[i] * p[j]; break; }
else phi[i * p[j]] = phi[i] * phi[p[j]];;
}
}
}
LL calc(LL x)
{
LL ret = 0;
for (LL i = 1; i <= x; )
{
LL r = min(x / (x / i), x);
ret += (r - i + 1) * (x / i);
i = r + 1;
}
return ret;
}
int main()
{
scanf("%lld", &n);
n = 1e15;
filter();
for (LL i = 1; i * i <= n; ++ i)
{
ans += phi[i] * calc(n / (i * i));
}
printf("%lld\n", ans);
return 0;
}
杜教筛
基本
目的:求\(f(i)\)的前缀和
套路:构造\(h=g*f\)
即等同于枚举两个因数,他们乘积\(\le n\),计算他们\(g\)和\(f\)的乘积的和
设\(S(n)=\sum f(i)\)
所以只要凑出方便的\(h, g\)就可以套用最后一行式子了
求\(\sum_{i=1}^{n}\mu(i)\)
\(\epsilon = \mu * 1, h=\epsilon,g=1\)
求\(\sum_{i=1}^{n}\phi(i)\)
\(Id=\phi*1, h=Id, g=1\)
先看一道例题
例题BZOJ3944: Sum
\(T(\le 10)\)组数据, \(N(\le 2^{31}-1)\), 求\(\sum_{i=1}^{n}\mu(i), \sum_{i=1}^{n}\phi(i)\)
两个长得很像可以一起做, \(\lfloor\frac{n}{i}\rfloor\)可以整除分块
递归实现, 但是要先预先筛一部分, 不然会\(T\), 记忆化搜索用\(map\)记录
#include <cstdio>
#include <map>
using namespace std;
typedef long long LL;
typedef pair<int, LL> PIL;
#define mkpr make_pair
const int MAXN = 5e6 + 2;
inline LL in()
{
LL x = 0, flag = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') flag = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x * flag;
}
int n;
int tot;
bool vis[MAXN];
int p[348516], mu[MAXN]; //348513
LL phi[MAXN];
void filter()
{
phi[1] = mu[1] = 1;
for (int i = 2; i < MAXN; ++ i)
{
if (!vis[i]) { mu[i] = -1, phi[i] = i - 1, p[++ tot] = i; }
for (int j = 1; j <= tot && i * p[j] < MAXN; ++ j)
{
vis[i * p[j]] = true;
if (i % p[j] == 0)
{ mu[i * p[j]] = 0, phi[i * p[j]] = phi[i] * p[j]; break; }
else
mu[i * p[j]] = -mu[i], phi[i * p[j]] = phi[i] * phi[p[j]];
}
}
for (int i = 2; i < MAXN; ++ i) mu[i] += mu[i - 1], phi[i] += phi[i - 1];
}
map <LL, PIL> f; //92680
PIL solve(LL x) // Smu[x] = 1 - [2, n]Smu[x / i],
{
if (x < MAXN) return mkpr(mu[x], phi[x]);
if (f.count(x)) return f[x];
int mu = 1; LL phi = x * (x + 1) / 2;
for (LL i = 2, nex = i; i <= x; i = nex + 1)
{
nex = x / (x / i);
PIL val = solve(x / i);
mu -= (nex - i + 1) * val.first;
phi -= (nex - i + 1) * val.second;
}
return f[x] = mkpr(mu, phi);
}
int main()
{
filter();
// printf("%d\n", tot);
int Test = in();
while (Test --)
{
n = in();
PIL ans = solve(n);
printf("%lld %d\n", ans.second, ans.first);
}
return 0;
}
复杂度
不会证, 据说预处理\(n^{\frac{2}{3}}\), 复杂度为\(O(n^{\frac{2}{3}})\)