数论卷积相关
积性函数
如果函数是积性函数,那么只需要知道其在 \(1, p^k\) 处的取值,那么已经完全了解了这个函数。这极大简化了问题。
随便看个例子:求因子和函数 \(\sigma\) 是什么。
首先证明是积性函数。考虑 \(\operatorname{id} * \mathbf 1 = \sigma\),因此是积性函数。
其次考虑其在 \(1,p^k\) 处取值:
因此可以乘起来就知道了。
接下来再看看欧拉函数是什么。怎么证明积性函数:
那是什么就乱证。
逆元,单位元的定义
单位元 \(e\),也叫做幺元。其在一个半群 \((D, \circ)\)(前一个是元素集合,后一个是运算符)中被定义的话,半群被称作幺半群。
某一个元素 \(x\) 的逆元 \(x^{-1}\),意思是 \(x \circ x^{-1} = e\)。
例如,
对于 \(\circ = +\),\(e = 0\),因此 \(x + x^{-1} = 0\)。
对于 \(\circ = \times\),\(e = 1\),因此 \(x \times x^{-1} = 1\)。
对于 \(\circ = \times\)(矩阵乘法),$e = $ 单位矩阵(主对角线是 \(1\) 其他都是 \(0\))。
Dirichlet 卷积
也叫乘法卷积。
对于两个数列 \(\{a_n\}, \{b_n\}\),定义 \(\{c_n\}\) 为 \(a,b\) 做狄利克雷卷积的结果。也称 \(c = a * b\)。那么 \(\forall i, c_i = \sum \limits_{k | i} a_k b_{\frac{i}{k}}\)。朴素地进行一次狄利克雷卷积,时间复杂度为 \(O(n \ln n)\)。
性质:
对于两个积性函数 \(f, g\),\(f*g\) 是积性函数。容易证明。(有什么用?积性函数有一些牛逼的筛法和性质)
在此基础上,定义一些特殊函数。
\(\mathbf 1\) 函数:\(\mathbf 1_x = 1\),是完全积性函数。
\(e\) 函数:半群 \((f, *)\) 的单位元。考虑其性质:
\(e * f = f\)
当 \(x=1\) 时,\(e_1f_1=1\),显然 \(e_1=1\)。
当 \(x\neq 1\) 时,\(e_1f_x + e_?f_?... = 1\),显然 \(e_? = 0\)。
因此 \(e = \{1, 0, 0, ..., 0\}\)。
\(\mu\) 函数:也叫做莫比乌斯函数。是 \(\mathbf 1\) 函数的逆元。也即,\(\mathbf 1 * \mu = e\)
当 \(x=1\) 时,\(\mathbf 1_1 \mu_1 = 1\),显然 \(\mu_1 = 1\)。
当 \(x=p\) 时,\(\mathbf 1_1 \mu_p + \mathbf 1_p \mu_1 = 0\),显然 \(\mu_p = -1\)。
为了简便,我们发现可以省去 \(\mathbf 1_?\)。接着看看。
当 \(x=p^k\) 时,\(\mu_1 + \mu_p + \mu_{p^2} + \mu_{p^3} + ...\),那么当 \(k>1\) 的时候 \(\mu_{p^k} = 0\)。
当 \(x=pq\) 时,$\mu_1 + \mu_p + \mu_{q} + \mu_{pq} $,那么 \(\mu_{pq} = 1\)。
二项式反演得,\(\mu_{p_1p_2p_3...p_k}=(-1)^k\)。
当 \(x=p_1^{a_1}p_2^{a_2}...p_k^{a_k}\) 时,需要 \(\mu_{有平方因子的数}=0\)。
验证一下,确实能对的上。
因此 \(\mu_x =
\left\{
\begin{array}{ll}
(-1)^k & x 没有平方因子,有 k 个质因数 \\
0 & x 有平方因子
\end{array}
\right.
\)
有一个东西是要注意到的。\(\sum \limits_{d | n} f_d\) 等于 \(f *\mathbf 1\)。
莫比乌斯反演
\(f * 1 = g, g * \mu = f\),第二个过程就是莫比乌斯反演。
有什么作用?对于某一项的性质可以追踪。例如 \(\mathbf 1_x * \mu_x = e_x\),可以判断 \([x=1]\)。
莫比乌斯函数是积性函数,可以通过线性筛求。其中素数赋 \(1\),最小质因子乘上的时候赋 \(0\),否则赋相反数。
POI2007 Zap(BZOJ1101)
整除分块即可。这是非常经典的应用。虽然在 \(a = b\) 的时候具有 \(2 \sum φ_i - 1\) 的简便方法,但是这个推法显然是更为能够推广的。
拓展式子
\(\varphi * \mathbf 1 = \operatorname{id}\),其中 \(\operatorname{id}_x = x\)。
证明:
首先这三个都是积性函数,因此只需证明对任意 \(p^k\) 都成立,那么对所有正整数均成立。
考虑 \(\varphi_{p^k} = p^{k-1}(p-1)\)。
因此 \(\varphi_{p^k} * \mathbf 1 = \sum \limits_{i = 1}^{k} p^{i-1} (p-1) + 1\)
得证。
狄利克雷前缀和
更优秀的求 \(a * \mathbf 1\) 的方式。用 \(p\) 进赋值序列的视角看待,是一个高维前缀和。注意 \(1\) 没啥特别的,没有 \(1\) 这一维度。
时间复杂度 \(O(n \log \log n)\),比一般的 \(O(n \log n)\) 要好。
P5495
f(i, 1, cnt) for(int j = p[i]; j <= n; j += p[i]) a[j] += a[j / p[i]];
类似地,可以卷 \(\mu\),也就是前缀和的逆运算。还不用筛。
f(i, 1, cnt) for(int j = n / p[i]; j >= 1; j --) a[j * p[i]] -= a[j];
P2714
gcd 卷积。 \(\sum \limits_{\gcd(i, j) = 1} a_ib_j\)
这个算超集和,就是 FMT。不需要显式的 \(p\) 进赋值序列,只需要直接枚举 \(p\) 即可。
注意卷积里面 \(a,b\) 分别是多少。
最后容斥做的时候一定要推清楚。系数是一个组合数行求和形式。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
//#define cerr if(false)cerr
#define freopen if(false)freopen
#define watch(x) cerr << (#x) << ' '<<'i'<<'s'<<' ' << x << endl
void cmax(int &x, int y) {if(x < y) x = y;}
void cmin(int &x, int y) {if(x > y) x = y;}
//调不出来给我对拍!
const int v = 10000;
int p[10010]; bool c[10010]; int cnt; int a[10010], b[5][10010];
void euler() {
f(i, 2, v) {
if(!c[i]) {p[++cnt] = i;}
for(int j = 1; j <= cnt && p[j] * i <= v; j ++) {
c[p[j] * i] = 1;
if(i % p[j] == 0) break;
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
//freopen();
//freopen();
//time_t start = clock();
//think twice,code once.
//think once,debug forever.
euler();
int n;
while(cin >> n) {
memset(a, 0, sizeof(a)); memset(b, 0, sizeof(b));
f(i, 1, n) {cin >> a[i]; b[1][a[i]] ++;}
//对 b 做 gcd 卷积
f(i, 1, v) b[0][i] = b[1][i];
f(j, 1, cnt) { //维度
for(int k = v / p[j]; k >= 1; k --) {
b[0][k] = b[0][k * p[j]] + b[0][k];
}
}
f(i, 1, 3) {
//FWT
f(j, 1, v) b[i + 1][j] = b[i][j];
f(j, 1, cnt) { //维度
for(int k = v / p[j]; k >= 1; k --) {
b[i + 1][k] = b[i + 1][k * p[j]] + b[i + 1][k];
}
}
//点值乘法
f(k, 1, v) b[i + 1][k] *= b[0][k];
//IFWT
f(j, 1, cnt) {
for(int k = 1; k <= v / p[j]; k ++) b[i + 1][k] = b[i + 1][k] - b[i + 1][k * p[j]];
}
}
b[2][1] -= b[1][1]; b[2][1] /= 2;
b[3][1] -= b[1][1]; b[3][1] -= 6 * b[2][1]; b[3][1] /= 6;
b[4][1] -= b[1][1]; b[4][1] -= 14 * b[2][1]; b[4][1] -= 36 * b[3][1]; b[4][1] /= 24;
cout << b[4][1] << endl;
}
//time_t finish = clock();
//cout << "time used:" << (finish-start) * 1.0 / CLOCKS_PER_SEC <<"s"<< endl;
return 0;
}
/*
2023/x/xx
start thinking at h:mm
start coding at h:mm
finish debugging at h:mm
*/
狄利克雷除法
递推,先解出 \(G\),时间复杂度 \(O(n \log n)\),然后卷。
总时间 \(O(n \log n)\)。
性质
狄利克雷卷积有交换律和结合律。(这个乱证)
两个积性函数做狄利克雷除法的结果是积性函数。
powerful number 筛
powerful number,是没有非平方因子的数。\(\le n\) 的 powerful number 的规模是 \(O(\sqrt n)\) 的。
时间复杂度 \(O(\sqrt n \times t)\),\(t\) 是查询一次 \(g\) 前缀和的时间。
2.2 模拟赛 T2 数论导数
【题意】
【分析】
怎么求 \(q_d\) 在 \(d \in powerful~number\) 的时候的取值?首先 \(q\) 是积性函数。因此只需要知道 \(q_1\) 和 \(q_{p^k}\) 的取值就可以乘出所有取值。注意 \(k > 1 \or k = 0\) 对所有 \(p\) 都成立。
积性函数的性质非常神奇,一定要懂得用。