2024 暑假学习笔记
线性代数
向量
我们定义向量是多维空间中一条带方向的线段,由于不太需要考虑其绝对位置关系,只考虑相对位置,一般都是平移到原点然后记录终点的坐标,记为 \(\vec x = (a_1, a_2, ..., a_n)\)。
一般来说我们只探讨二维向量,因为是比较容易想的。
比如说:

我们可以称这个向量为 \(u\),也可以表示为 \(\vec B\),显然 \(\vec B = (2, 3)\)。
同时在空间中也有一些特殊的向量,比如:
- \(\vec 0\),就是原点,由原点指向原点,初始的向量。
- \(\vec i\),在 \(x\) 轴方向前进一步的向量。
- \(\vec j\),在 \(y\) 轴方向前进一步的向量。
- \(\vec k, l, m ...\),在更高维度上前进一步的向量。
我们称这些向量为基向量。
如图:

向量 \(\vec i\) 和 \(\vec j\) 就是基向量。
下面介绍一下向量的加减法则:
- \(\vec a + \vec b = (x_a + x_b, y_a + y_b)\)。
- \(\vec a - \vec b = (x_a - x_b, y_a - y_b)\)。
如图:

\(\vec a + \vec b = \vec c\)。
如果是减法我们就加上其相反向量即可。
向量的数乘:
- \(p \vec a = (px_a, px_b)\)。
向量的点乘:
- \(\vec a \cdot \vec b = |a| \cdot |b| \cdot \cos <a, b>\)。
矩阵
\(n \times m\) 的矩阵。
\(n \times n\) 的矩阵称之为方阵。
运算:
- 矩阵的加法:按位相加即可。
- 矩阵的数乘:按位相乘即可。
- 矩阵的点乘:\(c_{i, j} = \sum a_{i, k} \times b_{k, j}\)。
- 矩阵的转置:写为 \(A^T\),\(A^T_{i, j} = A_{j, i}\)。
基础矩阵:
- 单位矩阵:即主对角线上全为 \(1\),乘上任何矩阵都为它本身。
- 零矩阵:全为 \(0\) 的矩阵,乘上任何矩阵都为零矩阵。
很多 DP 转移可以写成矩阵形式,由于矩阵乘法是有结合律的,所以可以快速幂做。
唯一有用的可能是高斯消元,那东西必须得看代码。
行列式
行列式仅针对方阵。
然后计算公式是:
一些性质:
- 交换两行,行列式值乘上 \(-1\)。
- 倍加,行列式值不变。
- 和的拆分(一行或一列),直接拆分相加即可。
- \(\det(AB) = \det(A) \det(B)\)
根据性质,可以用高斯消元把下面全部消成 \(0\),然后就只能取对角线上的值了。
LGV 引理
- Pro:给定一个具有特殊性质的平面图 DAG,求从初始点集到结束点集的不相交路径方案数(两个点集点的个数相同)。
特殊性质:将起始点集以及结束点集像二部图一样分开排列,将线拉直不相交(点集内部不连边,有的话就去除,反正无论如何也不会走,因为不相交)。
网格图是其中一种。
- 结论:定义 \(w(P)\) 为路径 \(P\) 上边的权的乘积(初始全为 \(1\)),\(f(s, t)\) 表示 \(s\) 到 \(t\) 可能的路径之和,\(A\) 为初始点集,\(B\) 为结束点集,那么:
即这个矩阵的行列式。
- 证明:咕咕咕。
Matrix-Tree 定理
-
Pro1:给你一张无向图,求其生成树个数。
-
结论:令 \(D\) 为度数矩阵(主对角线为度数,带权),\(L\) 为邻接矩阵(可以有权),\(A = D - L\), 然后选择 \(A\) 中的一个格子,删掉它所在列和行,那么 \(ans = \det(A)\)。
-
证明:咕咕咕。
-
Pro2:给你一张有向图,求其外向/内向生成树个数。
-
结论,外向:\(ans = \det(A = D_{in} - L)\),内向:\(ans = \det(A = D_{out} - L)\)。
-
证明:咕咕咕。
BEST 定理
最好的定理!!!
-
Pro:给你一张有向图,要你求从 \(x\) 出发的欧拉回路条数。
-
结论:满足(\(D_x\) 为以 \(x\) 为根的内向生成树个数):
- 证明:咕咕咕。
线性基
基可以理解为基向量,线性基通常是关于异或的基。
然后就是做高斯消元,矩阵中剩下的数就是线性基所要的数了,然后表示一下即可。
然后我们可以用线性基求所有数的最大异或值和最小异或值,直接贪心即可。
如果要求第 \(k\) 小/大异或值,那么可以重新调整一下基然后再像 0/1 Trie 一样搞,不过通常只会考前两个。
拉格朗日插值
即给出 \(n\) 个点能确定一个 \(n - 1\) 次多项式,那么此时可以通过以下公式求这个多项式的点值:
其实通常我们取连续值然后用阶乘计算,那么计算单个就变成了 \(O(n)\) 的了。
一般如果有式子那么就猜多项式系数,直接开到最大的做就完事了。。。
题目
P2044 [NOI2012] 随机数生成器
- Description:给定 \(X_0, m\) 和递推公式 \(X_{n + 1} = (aX_n + c) \mod m\),求 \(X_n\),\(1 \le n \le 10^18\)。
首先这个题我们肯定要用矩阵快速幂,考虑构造矩阵:
然后初始矩阵为:
然后答案就是 \(P^n \cdot Q\)。
P1962 斐波那契数列
- Description:求斐波那契数列第 \(n\) 项,\(1 \le n \le 10^9\)。
直接上矩阵,不多说。
P2447 [SDOI2010] 外星千足虫
- Description:给你若干个方程,形如一堆数的和 \(\bmod 2\) 的值,让你解方程,并推算出最早可以解出的时刻。
其实我们不难发现可以转化成异或方程组,然后我们可以高斯消元解出来,也可以用线性基求解。
但是题目太毒瘤了,我们需要用 bitset 优化一下常数。
P2886 [USACO07NOV] Cow Relays G
- Description:给你一张有向图,让你求从 \(S\) 到 \(T\) 恰好经过 \(K\) 条边的最短路。
我们定义广义矩阵乘法为 Floyd 的转移形式,然后我们把邻接矩阵对着这个东西求 \(K\) 次快速幂,最后 \((S, T)\) 的值就是答案了。
P1397 [NOI2013] 矩阵游戏
- Description:有些复杂,可以自己看原题。
其实就是每一行矩阵快速幂,然后换行的时候乘上另外一个矩阵,对 \(m\) 行都做一遍这个事情,发现也可以矩阵快速幂,简单来说就是把式子完全展开然后构造两个矩阵硬算就好了。
P3746 [六省联考 2017] 组合数问题
- Description:让你求 \(\sum_{i = 0}^{n} C_{nk}^{ik + r} \bmod p\)。
其实我们组合意义一下,就是在 \(nk\) 个不同物品中选 \(t\) 个,且满足 \(t \bmod k = r\) 的方案数有多少,构造矩阵 DP 即可。
P6573 [BalticOI 2017] Toll
- Description:有些复杂,可以自己看原题。
考虑到 \(k\) 最多为 \(5\),所以我们每 \(5\) 个构造一个与下 \(5\) 个的矩阵,然后求 \(s\) 到 \(t\) 的最短路就是直接把两个所在矩阵中间的矩阵全部乘起来(广义矩阵乘法:Floyd 更新方式),然后看对应位置。
考虑到矩阵乘法是有结合律的,所以我们多次询问采用线段树进行解决。
P3193 [HNOI2008] GT考试
- Description:有些复杂,可以自己看原题。
考虑设状态 \(f_{i, j}\) 为算到第 \(i\) 位,后 \(j\) 位与 \(t\) 匹配的方案数,这个时候我们需要求一个数组 \(g_{i, j}\),表示匹配了 \(i\) 位后再加一个字符能匹配 \(j\) 位的方案数,用 KMP 可以快速算出,然后对 DP 进行矩阵快速幂即可。
P7736 [NOI2021] 路径交点
- Description:有些复杂,可以自己看原题。
我们发现本质就是 LGV 引理里的容斥,利用 LGV 引理做,完事。
P3317 [SDOI2014] 重建
- Description:有些复杂,可以自己看原题。
我们发现 Matrix-Tree 定理本质上是要求生成树权乘积和的,那么我们考虑题目让我们求什么:
就是还要保证生成树以外的边不被选中。
然后共同除以 \(1 - p_e\),然后再乘上,变成:
P4336 [SHOI2016] 黑暗前的幻想乡
- Description:有些复杂,可以自己看原题。
我们发现容斥 + Matrix-Tree 定理即可。
就是 \(n - 1\) 家公司边的生成树 - \(n - 2\) + \(n - 3\)。
为什么要容斥呢?因为可能有多家公司用很多边,根据直觉,就应该这样容斥。
组合计数
很简单的一些公式:
- 递推公式:
- 吸收恒等式:
- 上指标求和(利用递推公式,加一项):
- 平行恒等式:
- 范德蒙德卷积:
- 二项式定理:
来看几个问题:
- 问题 \(1\):给你 \(n\) 个相同的球和 \(m\) 个不同的盒子,问有多少种办法把这 \(n\) 个球放进盒子?盒子不可以空。
利用插板法,不难得到 \(\binom{n - 1}{m - 1}\)。
- 问题 \(2\):给你 \(n\) 个相同的球和 \(m\) 个不同的盒子,问有多少种办法把这 \(n\) 个球放进盒子?盒子可以空。
我们借 \(m\) 个元素过来,就显然是 \(\binom{n + m - 1}{m - 1}\)。
- 问题 \(3\):有不定方程 \(X_1+X_2+···+X_n = S\),每个量都是非负整数,对于每个 \(X_i\), 有限制 \(X_i \le K\) ,计数这样的不定方程的解数。\(n, K \le 10^5\)。
考虑到容斥,所有的减去一个大于 \(K\),加上两个大于 \(K\),问题就把 \(S\) 减去若干个 \(K\) 就好了,但是需要注意有顺序,所以要乘上 \(\binom{n}{i}\)。
然后是格路计数,就仿照卡特兰数,画两条线,然后上面那根对折以下就好了。
然后需要注意的是如果是有上下界,直接容斥即可,不要犹豫。
然后是斯特林数(由于不会打所以直接括号表示):
- 第一类斯特林数:\((n, m)_1\) 表示 \(n\) 个不同元素构成 \(m\) 个圆排列方案。递推:
- 第二类斯特林数:\((n, m)_2\) 表示 \(n\) 个不同元素构成 \(m\) 个非空子集。递推:
需要特别特别特别记住的公式
利用这个公式我们可以推很多组合意义。
比如 Card 和自然数幂和(虽然不如直接拉插)。
题目
P1450 [HAOI2008] 硬币购物
- Description:给你 \(4\) 种硬币个数及面值,问恰好买 \(s\) 元物体有多少种方式?
考虑容斥,限制放的硬币种类,最开始再预处理一下多重背包就好了。
P1641 [SCOI2010] 生成字符串
- Description:\(n\) 个 \(0\) 和 \(m\) 个 \(1\),要求前 \(k\) 位 \(1\) 的个数不能少于 \(n\) 的个数,问方案总数。
考虑组合意义,就是网格图画线,然后翻折一下求方案个数,然后稍微容斥一下即可。
CF1278F Cards
- Description:有点复杂。
概率的题就是选中乘上选中的概率乘上没选中的乘上没选中的概率,推出来一下,然后用斯特林数幂展开,然后用二项式定理就做完了。
CF622F The Sum of the k-th Powers
- Description:求 \(\sum_{i = 1}^{n} i^k\)。
这个东西有个很明显的结论,就是它是 \(k + 1\) 次多项式,然后我们用拉格朗日插值插连续的 \(k + 2\) 个点值,算阶乘预处理就完了。
[AGC013E] Placing Squares
- Description:有点复杂。
考虑分两类点,一类点是标记点,一类是没有标记点,然后我们设置两个不同的矩阵转移 DP,由于一共只会有线性个标记点,所以我们只需要做 \(k - 1\) 次快速幂和 \(k\) 次乘法就完了。
CF1895F Fancy Arrays
- Description:有点复杂。
考虑容斥,容斥下线后发现 \(\le x - k + 1\) 的贡献就是 \((2k + 1)^{n - 1}\),然后 \(\le x\) 的就弄个矩阵优化 DP 就好了。
[ABC281G] Farthest City
- Description:有点复杂。
考虑我们将图分层做 DP,那么第 \(i\) 层的点只会连向第 \(i + 1\) 层或者第 \(i\) 层自己,记录一下层的数量的当前层的点数就好了,注意 \(n\) 点必须要在最后一层,所以要把剩余点数减 \(1\)。
数据结构
并查集
其实并查集的本质就是一颗树,只不过看你是只维护连通性还是维护其他信息。
种类并查集
就按照种类分层,然后不同种类之间互相连边,通常能够描述不同种类之间的矛盾关系。
带权并查集
就是树上每条边都带权,然后由于有边权,所以大部分情况下不能路径压缩,只能按秩合并。
笛卡尔树
考虑这样一个对于区间 \([l, r]\) 建树过程:
- 拎出最大值做根节点。
- 最大值两边的区间继续处理。
就是一个大根堆。
但是我们实际操作时不这么操作,因为复杂度是 \(O(n^2)\) 的。
我们这么处理:
for ( int i = 1; i <= n; i ++ ) {
int tmp = cnt;
while ( tmp && a[id[tmp]] > a[i] ) {
tmp --;
}
if ( tmp ) {
tree[id[tmp]].r = i;
}
if ( tmp < cnt ) {
tree[i].l = id[tmp + 1];
}
id[++ tmp] = i, cnt = tmp;
}
就每次试着往右边加,如果不行往上跳,复杂度 \(O(n)\)。
如果忘了也可以直接分治 + 线段树 \(O(n \log_2 n)\) 做,应该不会卡。
哦,还有就是笛卡尔树的启发式搜索复杂度是对的(每次都往小的区间遍历)。
左偏树
先建一个堆。
定义 \(dist_i\) 表示 \(i\) 节点向右最多能走的节点个数。
然后合并堆的时候不断往下合并,如果左儿子的 \(dist\) 小于了 右儿子,就交换。
为什么复杂度是对的,因为每一层都是满的(会交换),所以 \(dist\) 最多不超过 \(\log_2 n\)。
放个详细点的合并代码:
int merge(int x, int y){
if(!x || !y){
return x + y;
}
if(a[y] < a[x]){
swap(x, y);
}
rs[x] = merge(rs[x], y);
if(dist[ls[x]] < dist[rs[x]]){
swap(ls[x], rs[x]);
}
dist[x] = dist[rs[x]] + 1;
return x;
}
可持久化
可持久化的本质就是维护多个历史版本,将上一个版本需修改的节点原封不动的复制下载,继续更改。
然后可持久化数据结构复杂度必须是非均摊的,否则可以通过单一元素不同版本多次查询卡你(如 Splay)。
然后复杂度正确必须保证新增节点个数正确与结构正确。
可持久化字典树
一般是 O/1 Trie。
就先可持久化以下。
然后我们会发现这个东西对于某些差分问题有可减性,即对应节点相减可以把重复部分消掉,然后我们可以进行最大/最小/第 \(k\) 大的异或值的求解。
放个插入代码,可能有点不一样:
void insert ( int &node, int k, int x ) { // k 表示位数
tree[++ cnt] = tree[node];
tree[cnt].siz ++;
node = cnt;
if ( k == -1 ) {
return ;
}
insert ( tree[node].ch[( x >> k ) & 1], k - 1, x );
}
上述代码如果是第 \(i\) 个历史版本更改而来要把 \(rt_{now} = rt_i\)。
可持久化线段树
一般也是更改节点可持久化,能用标记永久化就尽量用标记永久化,pushdown 在一些情况下复杂度是对的也可以做。
可持久化权值线段树也利用可减性做题。
这个没有固定代码,就是修改的时候新增结点就好了。
可持久化平衡树
当分裂与合并的时候,复制一份,然后维护历史版本。
可持久化一般是 FHQ,但是这个东西的复杂度似乎不太对,但是很难卡,但是我知道可持久化 FHQ 的区间复制复杂度肯定是错的。
详细讲区间复制:把历史版本与当前版本合并就行。
来点合并分裂:
void split(int node, int val, int &r1, int &r2){
if(!node){
r1 = 0;
r2 = 0;
return ;
}
if(tree[node].val <= val){
r1 = newnode();
tree[r1] = tree[node];
split(tree[r1].rs, val, tree[r1].rs, r2);
update(r1);
}
else{
r2 = newnode();
tree[r2] = tree[node];
split(tree[r2].ls, val, r1, tree[r2].ls);
update(r2);
}
}
int merge(int x, int y){
if(!x || !y){
return x + y;
}
if(tree[x].key > tree[y].key){
int node = newnode();
tree[node] = tree[x];
tree[node].rs = merge(tree[node].rs, y);
update(node);
return node;
}
else{
int node = newnode();
tree[node] = tree[y];
tree[node].ls = merge(x, tree[node].ls);
update(node);
return node;
}
}
线段树 & 平衡树
线段树
主要是讲一些基本模型
考虑到线段树的基本结构,我们需要开 \(4n\) 个结点。
还有就是势能线段树,反正分析势能乱搞就对了(比如区间取 \(\min / \max\))
然后以下是基本总结:
线段树(Segment Tree)是一种基于树结构的数据结构,主要用于处理区间查询和区间更新操作。其基本原理如下:
数据结构:
- 线段树通常是一棵平衡二叉树。
- 每个节点代表数组中一段连续的区间,叶子节点对应数组中的单个元素。
构建过程:
- 线段树从根节点开始,递归地将数组划分为更小的子区间,直到每个叶子节点表示数组中的一个单独元素。
- 每个非叶子节点存储其子节点的合并结果(如区间和、最大值等)。
功能:
- 区间查询:线段树能快速求解任意区间的查询问题,如区间和、最小值、最大值等。
- 区间更新:支持快速更新数组中的元素,并且能够保持树结构的正确性。
操作复杂度:
- 构建:线段树的构建时间复杂度为 \(O(n)\),\(n\) 是数组长度。
- 查询:查询操作的时间复杂度为 \(O(\log n)\)。
- 更新:更新操作的时间复杂度为 \(O(\log n)\)。
应用场景:
- 适用于需要频繁进行区间查询和更新的场合,如动态统计区间内的最值、求和等问题。
- 在解决动态数组查询问题时,特别是针对静态区间的查询和更新操作时表现优异。
- 总结来说,线段树通过将数组元素组织成树结构,利用树的特性实现高效的区间查询和更新,是解决区间统计问题的一种经典数据结构。
平衡树
一般都是写 FHQ Treap。
如果是 Splay 就是单旋双旋旋到顶,可以动态适配复杂度(如 \(O(\log n)\) 的启发式合并),还有就是可以实现 LCT。
如果是替罪羊树就是重构,这一类平衡树在乱搞方面有极大优势(如 KDT 重构),但是基本不太好写,复杂度也有问题,但是好想。
然后附上分裂合并代码:
void split ( int node, int &r1, int &r2, int k ) {
if ( !node ) {
r1 = r2 = 0;
return ;
}
if ( tree[node].val <= k ) { // 如果已经小于,整个左子树也是满足的,分裂右子树
r1 = node;
split ( tree[node].rs, tree[node].rs, r2, k );
}
else { //不满足,右子树肯定满足 > k,合并到 r2,分裂左子树
r2 = node;
split ( tree[node].ls, r1, tree[node].ls, k );
}
pushup ( node );
}
int merge ( int x, int y ) { // 需保证 x 子树值比 y 子数值小
if ( !x || !y ) {
return x + y;
}
if ( tree[x].key < tree[y].key ) { // 随机合并,期望 log
tree[x].rs = merge ( tree[x].rs, y );
pushup ( x );
return x;
}
else {
tree[y].ls = merge ( x, tree[y].ls );
pushup ( y );
return y;
}
}
题目
P3261 [JLOI2015] 城池攻占
直接每个点维护左偏树,然后暴力往上合并即可,由于一个元素最多删除一次,所以复杂度有保证。
P1552 [APIO2012] 派遣
考虑到从下往上合并。
考虑一个人如果已经不能被选了,那么越往上走就越不能选,所以一个元素只会被丢一次,复杂度是对的。
P4755 Beautiful Pair
考虑笛卡尔树启发式合并,小的区间暴力,大的区间直接加入树状数组查询即可,注意要好好分析启发式合并的复杂度,否则会挂。
P3899 [湖南集训] 更为厉害
典题。
直接线段树合并统计贡献即可(发现深度是一段区间)。
P4592 [TJOI2018] 异或
对链和子树分别做可持久化 0/1 Trie 上的二分,然后就没了。
P3586 [POI2015] LOG
直接上平衡树统计一下贡献,判断一下贪心选择能否成功。
P4514 上帝造题的七分钟
考虑到空间限制不允许我们使用二维线段树,所以我们可以二维树状数组维护差分数组的四个项,然后二位前缀和一下。
数论
exgcd
通常来说,我们用辗转相除法求 \(\gcd (a,b)\)。
根据裴属定理,\(ax + by = c\) 有整数解当且仅当 \(\gcd ( a, b ) | c\),所以问题就被简化成了 \(ax + by = \gcd (i, j)\)。
然后我们考虑在辗转相除的过程中求,具体来说:
- 辗转相除已经解出 \(bx_1 + (a \bmod b)y_1 = \gcd ( a, b )\)
- 我们要求 \(ax_2 + b_x2 = \gcd ( i, j )\) 的解。
- 将上面化简得:\(bx_1 + (a - b\lfloor \frac{a}{b} \rfloor)x_2\)。
- 化简:\(ax_2 + b(x_2 - b \lfloor \frac{a}{b} \rfloor)\)。
- 然后继续递推就好了
Lucas 定理
就是拆成 \(p\) 进制下每一位的组合数相乘。
具体来说:
通常运用在模数较小,然后求逆元会挂得情况下(如果你看不懂,你得逆元得重新学)。
CRT
没用,不讲。
exCRT
就我们设两个东西公共解为 \(a\),列出二元一次方程,联立,然后不断重复解方程的过程就好了。
乘法逆元
若 \(ax \equiv 1 ( \mod p)\),则 \(a\) 与 \(x\) 在模 \(p\) 意义下互为乘法逆元。
使用场景:
- 若出现 \((\frac{a}{b} \bmod p)\),不能等价于 \(\frac{a \bmod p}{b \bmod p}\),此时可以用 \(a\) 乘以 \(b\) 的逆元 \(inv_b\),\(a \times inv_b \bmod p\)。
求解逆元:
- 求单个整数的逆元:
-
- 扩展欧几里得算法:
\(ax \equiv 1 ( \mod p) \Rightarrow ax + py = 1\)
- 由此可知,乘法逆元不一定存在,因为方程并不一定有解。
- 乘法逆元若存在,那么有无数个,但在模 \(p\) 意义下只有一个(最小解)。
-
- 有限情况下使用费马小定理:
费马小定理:若 \(p \in \mathbb{P}\),且 \(\gcd (a, p) = 1\),那么 \(a^{p - 1} \equiv 1 ( \mod p )\)
- \(a^{p - 1} \equiv 1 ( \mod p ) \Rightarrow a^{p - 2} \cdot a \equiv 1 ( \mod p )\),所以 \(a^{p - 2}\) 为 \(a\) 在模 \(p\) 意义下的逆元。
- 求 \(1 \sim n\) 的所有整数的逆元(前提是存在):
\(ax \equiv 1 ( \mod p )\),设 \(p = a \cdot q + r\),其中 \(q = \lfloor \frac{p}{a} \rfloor, r = p \bmod a\),则 \(p = a \cdot q + r \equiv 0 ( \mod p)\),\(a \equiv -r \cdot inv_q ( \mod p ), inv_a = -q \cdot inv_{p \bmod a}\),那么 \(inv_a = \frac{-q}{a} \cdot inv_{p \bmod a}\)
- 注意前提要存在。
- 线性求逆元:
- 先预处理阶乘,然后处理出 \(n!\) 的逆元,每次都乘上 \(i\) 就可以线性求解 \(1 \sim n\) 的阶乘的逆元了,这个用的一般比较多,但是大部分时候用 \(\log\) 就够了。
数论 \(2\)
基本数论
数论函数:定义域为正整数的函数。
欧拉函数:定义 \(\varphi(n) = \sum\limits_{i = 1}^n [\gcd(i, n) = 1]\)。
特殊点:
- \(\varphi(1) = 1\)。
特殊性质:
- 如果 \(p \in \mathbb{P}\),\(\varphi(p^n) = p^{n - 1}(p - 1)\),证明:
与 \(p\) 不互质的 \(x\) 一共有 \(p^{n - 1}\) 个(含有 \(p\)),所以 \(\varphi (p) = p^n - p^{n - 1}\),证毕。
- 若 \(a | x\),则 \(\varphi(ax) = ax\),证明:
分成 \(1 \sim x, 2x \sim 3x ...\) 若干个区间,我们设 \(1 \sim x\) 里有 \(p_1, p_2 ...\) 与 \(ax\) 互质,那么 \(p_1 + x, p_2 + x ...\) 仍然与 \(ax\) 互质,且只存在这些在 \(x \sim 2x\) 之间的数与 \(ax\) 互质,因为若有一个另外的数 \(p_3\),那么 \(p_3 - x\) 理应与 \(ax\) 也互质,推翻假设,证毕。
- 若 \(\gcd ( a, b ) = 1\),\(\varphi(ab) = \varphi (a) \varphi (b)\),证明:
欧拉函数是积性函数。
欧拉函数的求法:
- 用定义法求单个 \(x\) 的欧拉函数 \(\varphi( x )\),公式如下:
\(\varphi(x) = x \cdot \frac{p_1 - 1}{p_1} \cdot \frac{p_2 - 1}{p_2} \cdot ...\)。
- 同线性筛求 \(1 \sim n\) 以内所有数值的欧拉函数,具体操作:
void init () {
phi[1] = 1;
for ( int i = 2; i <= 1000000; i ++ ) {
if ( !vis[i] ) {
prime[++ cnt] = i;
phi[i] = i - 1;
}
for ( int j = 1; j <= cnt && i * prime[j] <= 1000000; j ++ ) {
vis[i * prime[j]] = true;
if ( !( i % prime[j] ) ) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * ( prime[j] - 1 );
}
}
}
莫比乌斯反演
就数论函数典型套路,然后讲莫比乌斯反演。
就是 \(\sum_{d | n }\mu(d) = [n = 1]\)。
然后记住一些数论函数间的乘积关系,不过大部分没用。
然后一些典型推柿子:
- 转换变量,枚举最大公约数。
- \(d(ij) = \sum_{x | i} \sum_{y | j} [\gcd(x, y) = 1]\)
- 然后进行区域划分,分块套分块。
- 整理式子,改变顺序。
- 充分理解。
没有什么别的,就是推式子。
然后放一些经典例题(其实杜教筛学没学都一样,反正不会考)。
题目
整除分块
记录一下:\(r = n / ( n / l ), l = r + 1\)。
主要就是利用除法的连续性。
P3327 [SDOI2015]约数个数和
又是一道清新的莫比乌斯反演(真是令人作呕)。
Tm 的想了 \(5\) 分钟没有想到怎么推柿子结果是 \(d\) 的性质没了解到。这道题会 \(d\) 的性质就秒秒钟切。
\(d\) 的性质:
有了这个性质就秒秒钟切,先令 \(n \le m\):
根据直觉可得:
很显然 \([p|x][p|y] = 0\) 是无用的:
然后发现 \(\left\lfloor \frac{n}{p i} \right\rfloor = \left\lfloor \frac{\frac{n}{p}}{i} \right\rfloor\),此时你可以预处理出里面的数,外面和里面可以用整除分块搞。
P4213 【模板】杜教筛(Sum)
包含着很多悲伤的故事(指卡 long long)。
考虑对于任意积性函数 \(f(x)\) 来说,如果你要求 \(S(n) = \sum\limits_{i = 1}^n f(i)\),你该怎么办。
正常做法是 \(O(n)\),毒瘤们非常不爽,于是有了杜教筛。
首先我们需要知道狄利克雷卷积是什么:
我们定义 \(f, g\) 为两个数论函数,则 \(h = f * g\),有如下等式:
其实这个东西可以化成卷积的形式:
只不过上面那种形式比较通用。
考虑杜教筛的原理,先构造 \(h, g\),使得 \(h = f * g\),则:
然后:
然后我们发现 \(S\left(\left\lfloor\frac{n}{d}\right\rfloor\right)\) 是可以整除分块算的,我们只需快速算出 \(\sum_{i = 1}^n h(i)\) 即可,然后我们递归求解即可。
其复杂度为 \(O(n^{\frac{2}{3}})\),其实没啥用。
P5221 Product
感觉很难欸。
首先根据我们的瞪眼法不难发现 \(104857601\) 是一个质数,这在后面非常有用。
然后你发现 \(\text{lcm}\) 这东西,我熟啊,直接推柿子:
先看分子:
解释一下这里为什么,你考虑当 \(i\) 不变的情况下,$j = 1, 2, ..., n $ 的情况就 OK 了。
然后再看分母(公式警告):
这里很有道理吧!
然后我们只看指数:
我们发现,对于每个 \(i\) 来说,有贡献的只有与他互质的数,这种计数方法在 GCD SUM 这道题里有所体现:
那么我们便要求:
然后你提交一发,发现 long long 会炸!所以我们需要用到欧拉定理:
当 \(\gcd (a, k) = 1\) 时,有 \(a^b = a^{b \bmod \varphi(k)}\),此时我们的模数时质数,所以给指数模一下 \(104857601 - 1\) 就行了。
然后就是疯狂注意细节。

浙公网安备 33010602011771号