数学专题集训笔记
感谢lsy学长来101给我们上课~
Day 1
逆元
对于一个 \(a\),当 \(ab\equiv1\pmod{m}\) 时我们把 \(b\) 的最小整数解称作 \(a\) 模 \(m\) 的逆元,记作 \(a^{-1}\) 或 \(\frac{1}{a}\)。
接下来我们来看看逆元的求法。
费尔马小定理
如果 \(a\) 是一个整数,\(p\) 是一个质数,则有
所以对于我们上面要求的形式的话,那么有
所以在模数是质数的情况下,我们可以通过 \(O(\log{n})\) 的时间来快速幂求出一个数的逆元。优点是很好写,但是限制会有点大。
扩展欧几里得(exgcd)
扩欧是在基于裴蜀定理之后得到的结论,这里先说一下裴蜀定理说明的东西。
对于一个二元不定方程 \(ax+by=c\),该方程的充要条件是 \((a,b)|c\)
顺便再提一下逆元存在的充要条件:
如果 \(a\) 模 \(m\) 的逆元存在,则一定满足 $ (a,m)=1$,即 \(a,m\) 互质
而扩欧能够做的是找到一个 \(ax+by= (a,b)\) 的整数解。
首先,我们根据辗转相除法可以得到 $ (a,b)= (b,a\bmod b)$。
则我们按照递归的方法进去,假设我们找到了这样的一组解,然后我们考虑如何向上更新:
则可以这样:
那我们如果要求 \(a\) 的逆元的话只需要求 \(ax+my= (a,m)=1\),之后再将 \(x\) 取到最小自然数解即可。
扩欧的好处是它不需要满足给定的模数一定是质数的条件,在某些题目中如果多次给出模数且没有保证是质数的话那么它就有优势。可是它的时间复杂度还是 \(O(\log{n})\)。
如果某些毒瘤题目的时间要求卡的特别死的话,且要求大量的求逆元,同时模数的范围还不大的话就可以使用下面的方法。
线性求逆元
对于某些模数不大但要大量求逆元的题目,我们可以考虑 \(O(n)\) 预处理出所有的逆元,之后 \(O(1)\) 查询。
首先 \(1^{-1}\equiv 1\pmod{m}\)。即 \(1\) 的逆元是它本身。
之后我们设 \(m=kx+b\),也就是我们要求 \(x\) 的逆元。
则 \(kx+b\equiv 0\pmod{m}\)
之后我们两边同乘 \(x^{-1}b^{-1}\)
然后可以往下推:
然后我们保存下 \(inv\) 数组,之后因为 \(n\bmod x<x\),所以 \((n\bmod x)^{-1}\) 在之前求过,所以可以 \(O(m)\) 预处理。但是这种的问题就是如果 \(m\) 很大,就不太好办了。
卢卡斯定理/Lucas 定理
直接结论:
设 \(l_n^m=C_n^m\bmod p\),\(C_n^m\) 为组合数 \(\frac{n!}{m!(n-m)!}\),\(p\) 为质数。
则有:
数论函数与积性函数
一个定义整数集合上的函数叫做数论函数。
对于一个函数 \(f(n)\),如果 \(f(a)f(b)=f(ab)\),则称 \(f(n)\) 是完全积性函数。
我们怎么判断一个函数是不是积性函数呢?根据算数基本定理一个大于 \(1\) 的正整数 \(n\) 都可以表示成 \(p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}\cdots p_k^{\alpha_k}\),其中 \(\alpha_i\) 是正整数,\(p_i\) 是质数。所以一个函数 \(f(n)\) 是积性函数的充要条件为 \(f(1)=1\) 且 \(f(n)=f(p_1^{\alpha_1})f(p_2^{\alpha_2})\cdots f(p_k^{\alpha_k})\)
下面给出一些常用的积性函数:
- 常数函数 \(1(n)=1\)
- 恒等函数 \(id(n)=n\)
- 单位函数 \(\varepsilon(n)=[n=1]\)
- 欧拉函数 \(\varphi(n)=n\prod\limits_{c|n}(1-\frac{1}{c})\) 或 \(\varphi(n)=\sum\limits_{i=1}^{n}[(i,n)=1]\)
- 因子函数 \(d(n)=\prod\limits_{i=1}^k(a_i+1),a_i为 n 的质因数分解后 p_i 的指数\)
- 除数函数(因数和) \(\sigma(n)=\prod\limits_{c|n} c\)
- 莫比乌斯函数 \(\mu(n)=\begin{cases}1&n=1\\(-1)^k&n=p_1^1p_2^1\cdots p_k^1,\forall i\in[1,k],p_i\in\mathbb{P}\\0&otherwise\end{cases}\)
另外,积性函数都可以通过线性筛来求,只需要求出定义中质数的情况,与是否包含最小质因子的两种情况进行分类讨论即可。
莫比乌斯函数的特殊性质
【证明】
首先当 \(n=1\) 时命题显然成立,现在论证当 \(n=1\) 时上式为 \(0\)。对于一个 \(n\) 只有当 \(c\) 恰好为 \(c=p_1^1p_2^1\cdots p_d^1\) 时才有贡献。则对于 \(n=p_1^1p_2^1\cdots p_k^1\),我们可以从中任取质因子,即我们要论证 \(\sum\limits_{i=0}^k (-1)^iC_k^i=0\)。根据二项式定理就可以求出上式等于 \(2^{k-1}-2^{k-1}=0\)。
欧拉函数的特殊性质
【证明】
我们首先来考虑 \(\varphi(n)\) 的线性筛方法。对于一个质数 \(p\),显然 \(\varphi(p)=p-1\)
而对于 \(kp\) 与它不互质的数是 \(1,p,2p\cdots kp\),一共 \(k\) 个,所以 \(\varphi(kp)=kp-k=k(p-1)\)。之后考虑 \(\varphi(p^k)\),由上一条得 \(\varphi(p^k)=p^{k-1}(p-1)=p^k-p^{k-1}\)。
然后我们思考 \(\sum\limits_{c|p^k}\varphi(c)\),显然 \(c\) 的取值只有 \(1,p,p^2\cdots p^k\),所以 \(\sum\limits_{c|p^k}\varphi(c)=(\sum\limits_{i=1}^k \varphi(p^i))+1=(\sum\limits_{i=1}^k (p^i-p^{i-1}))+1=p^k-p^{k-1}+p^{k-1}-p^{k-2}+\cdots-p^1+p^1-p^0+1=p^k\)。之后因为积性函数的性质 \(\varphi(ab)=\varphi(a)\varphi(b)\),所以根据算数基本定理的话 \(\sum\limits_{c|n} \varphi(c)=\prod\limits_{i=1}^k \sum\limits_{c|p^i}\varphi(c)=n\)。
狄利克雷卷积
这个东西是一种定义在数论函数中的一种二元计算。对于两个函数 \(f(n)\) 与 \(g(n)\),它们的狄利克雷卷积是这样定义的:
显然这个东西满足交换律和结合律,当然它也有另一种表达方法:
特别的,\(\varepsilon(n)\) 是狄利克雷卷积的单位元,即对于所有 \(f(n)\),都有 \(\varepsilon*f=f\)
莫比乌斯反演
对于两个数论函数 \(f(n),g(n)\),如果 \(f(n)=\sum\limits_{c|n} g(c)\),则有 \(g(n)=f*\mu=\sum\limits_{c|n} f(c)\mu(\frac{n}{c})\)。
【证明】
首先根据上面莫比乌斯函数的性质,\(\varepsilon=1*\mu\),同时 \(f=g*1\),则有 \(g=g*e=g*(1*\mu)=g*1*\mu=(g*1)*\mu=f*\mu\),即 \(g(n)=f*\mu=\sum\limits_{c|n} f(c)\mu(\frac{n}{c})\)
Day 2
中国剩余定理(CRT)
给定 \(n\) 组非负整数 \(a_i, b_i\) ,求满足下面的所有方程的一个 \(x\) 在模 \(a_1a_2\cdots a_n\) 意义下的最小非负整数解。保证了 \(a\) 中所有元素两两互质。
对于这题,我们使用中国剩余定理来解决。特别的,模数必须要求两两互质,否则需使用扩展中国剩余定理。
对于两个方程 \(x\equiv b_1\pmod{a_1}\) 与 \(x\equiv b_2\pmod{a_2}\),首先通过扩欧求 \(b_1u_1+b_2u_2=1\) 的两个未知数 \(u_1,u_2\),之后可以得到一个新的方程 \(x\equiv b_2m_1u_1+b_1a_2u_2\pmod{a_1a_2}\)。我们将它与 \(x\equiv b_3\pmod{a_3}\) 对上面两个方程替换,之后重复进行操作。
最终我们将得到一个 \(x \bmod a_1a_2\cdots a_n\) 在满足上面方程的最小自然数解。
扩展欧拉定理
首先说明欧拉定理:
当 \(a,b,m\in \mathbb{Z}\) 且 \((a,m)=1\) 时有 \(a^{\varphi(m)}\equiv 1\pmod{m}\),但可以得知另一个重要性质 \(a^b\equiv a^{b\bmod \varphi(m)}\pmod{m}\)。
因为 \(a^b=a^{\varphi(m)\lfloor\frac{b}{\varphi(m)}\rfloor+b \bmod \varphi(m)}=a^{\varphi(m)^{\lfloor\frac{b}{\varphi(m)}\rfloor}}a^{b \bmod \varphi(m)}=1\cdot a^{b \bmod \varphi(m)}=a^{b \bmod \varphi(m)}\) 。
扩展欧拉定理在 \(a,b,m\in\mathbb{Z}\) 时有这个特征:
【证明】
首先第一个条件就是自己显然成立。
考虑第二个条件的成立原因。当 \((a,m)=1\) 时 \(a^{b\bmod \varphi(m)+\varphi(m)}=a^{b\bmod \varphi(m)}a^{\varphi(m)}=a^{b\bmod \varphi(m)}=a^b\) 成立。
考虑 \(a,m\) 不互质的情况。首先证明当 \(a=p^k,p\in\mathbb{P}\) 时的情况。根据算术基本定理与积性函数的性质,可以得到对于任意一个 \(m=p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_k^{\alpha_k}\) 都可以通过每一个 \(p\) 使用扩展欧拉定理的结果乘起来。
讨论 \(p^{\alpha}\) 的情况。如果 \((a,p^\alpha)=1\) 则根据上述证明显然对答案没有影响。否则上文中欧拉函数的性质 \(\varphi(p^k)=p^{k-1}(p-1)\) 可以得到 \(\varphi(p^\alpha)\geq \alpha_i\) 则 \(b\geq \varphi(m)\geq \varphi(p^\alpha)\geq \alpha_i\),则 \(p^\alpha | a^{b\bmod \varphi(m)+\varphi(m)}\),即 \(a^b\equiv 0\pmod{p^\alpha}\)。证毕。
BSGS/EXBSGS
BSGS
BSGS(Baby Step Giant Step) 通常用来解这个方程:
在 BSGS 中,要求 \((a,m)=1\)。这是因为它需要用到欧拉定理。
首先,我们不妨设 \(x=kv-y,0\leq y<m\),则:
首先,\(x\in[1,m]\)(这个之后证明)。所以我们首先考虑枚举所有 \(ba^y\bmod m\) 的值,然后存储它是由 \(y\) 得到的。之后枚举 \(k\in[0,\lceil\frac{m}{v}\rceil]\) 计算 \(a^{kv}\bmod m\) 是否能在之前存储的 \(ba^y\bmod m\) 中找到对应的数,能的话输出。但是要注意 \(kv-y\geq 0\)。
【 \(x\in[1,p]\) 的证明】
因为 \(a,m\) 互质,所以有 \(a^b\equiv a^{b\bmod \varphi(m)}\pmod{m}\),所以 \(x<\varphi(m)\leq m\)。证毕。
EXBSGS
当 \((a,m)\neq 1\) 时,欧拉定理不行了,所以我们考虑另一种方法。我们首先希望把同余转成普通的等式,则有:
为了方便,记 \(c=(a,m)\)
根据裴属定理,该方程成立的必要条件是 \(c|b\)。这样无解情况判定完毕。这样的化我们可以考虑替换该方程为:
之后我们设 \(a^{'}=a^{x-1},m^{'}=\frac{m}{c},n^{'}=\frac{b}{c}\) 则可再次迭代。迭代到 \((a,m)=1\) 为止。
设迭代了 \(t\) 次,迭代中所有 \(c\) 的和为 \(C\),则原方程化为
所以我们用它去求 BSGS 即可。
BSGS/EXBSGS 的 tips
- 在 \(a=0\) 情况时要特判,因为它都没最大公约数。
- 某些化简后的式子是分式的要注意是否会有分母为 \(0\) 的情况
- 有一些迭代的式子要考虑初始的参数就是所求的情况,然后避免迭代。
例题
P2398 GCD SUM
题目大意
求
\(n\leq 10^5\)
思路
根据 \(\varphi(n)\) 的性质,这个公式可以转成这样:
显然 \(c| (i.j)\) 的充要条件是 \(c|i,c|j\)。所以它就是这样的:
这种题通常都是枚举 \(c\),所以转成这样:
前面的 \(\sum\limits_{c=1}^n\varphi(c)\) 可以直接线性筛得到,我们考虑后面的部分。首先我们考虑 \([c|i]\) 会有多少个成立,显然有 \(\lfloor\frac{n}{c}\rfloor\) 个,后面也是同理。所以最终我们只用求下面的公式即可:
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e5+5;
ll n,p[MAXN],tot;
ll phi[MAXN];
bool np[MAXN];
void prime(){
phi[1]=1;
for(int i=2;i<=n;++i){
if(!np[i]){
p[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot&&i*p[j]<=n;++j){
np[i*p[j]]=true;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
}
int main(){
cin>>n;
prime();
ll ans=0;
for(int i=1;i<=n;++i){
ans+=phi[i]*(n/i)*(n/i);
}
cout<<ans<<endl;
return 0;
}
P1891 疯狂 LCM
题目大意
求
多组数据,\(T\leq 3\cdot 10^5,n\leq 10^6\)。
思路
首先 \(lcm(i,j)=\frac{ij}{ (i,j)}\),所以原式化为
之后把 \(n\) 提出去
然后关键一步来了,这种题我们的通常做法都是将一个式子化为 \([ (i,j)=k]\) 的形式。
那么我们添一个 \(c\) 变成
然后结合到我们希望利用 \(\varphi(n)\),所以我们希望后面的表示的是一个互质关系,所以有这样:
对于这一堆分数,我们希望它整一些,所以我们可以直接枚举 \(\frac{i}{c}\):
之后因为 \(\frac{n}{c}\) 与 \(c\) 都是 \(n\) 的因数,所以可以直接变换:
\(\frac{i}{c}\):
这个东西显然不太好求,我们希望把这些 \(i\) 相加的东西凑的整一些。然后因为最大公约数有这样的性质:
对于两个数 \(a,b\),有 $ (a,b)= (b-a,b)$
换句话说,$ (i,c)= (c-i,c)$。也就是说,如果 \(i\) 可以,那么 \(c-i\) 也可以,这样我们就可以凑出 \(\frac{\varphi(c)}{2}\) 对 \(c\)。但是对于 \(c=1\) 来说,要特判一下这个贡献是 \(1\)。
所以我们可以用欧拉筛去预处理出所有数对它的倍数的贡献,然后直接 \(O(1)\) 输出。
时间复杂度 \(O(n\log{n}+T)\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e6+5;
ll n,p[MAXN],tot;
ll phi[MAXN];
bool np[MAXN];
void prime(){
phi[1]=1;
for(int i=2;i<=n;++i){
if(!np[i]){
p[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot&&i*p[j]<=n;++j){
np[i*p[j]]=true;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
}
ll ans[MAXN];
int main(){
n=MAXN-1;
prime();
for(int i=1;i<=n;++i){
for(int j=1;i*j<=n;++j){
if(i==1){
ans[i*j]++;
continue;
}
ll x=i*j;
ans[x]+=(phi[i]*i)/2;
}
}
ios::sync_with_stdio(false);
ll t;
cin>>t;
while(t--){
cin>>n;
cout<<ans[n]*n<<endl;
}
return 0;
}
P4139 上帝与集合的正确用法
题目大意
定义 \(a_0=1,a_i=2^{a_{i-1}}\),求 \(a_\infty\bmod m\) 的值。
\(1\leq m\leq 10^7\)
思路
对于这种幂塔取模,考虑扩展欧拉定理。我们可以思考用递归的方式求解,因为它有无数层,所以第一层的次幂肯定远远大于 \(\varphi(m)\),所以按照递归的方式去求扩展欧拉定理。等递归到 \(\varphi(m)=1\) 时就返回 \(1\) 即可。时间复杂度 \(O(\log{m})\)。
代码
#include<bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
const ll MAXN=1e7+5;
ll n=MAXN-2,p[MAXN/10],tot;
ll phi[MAXN];
bool np[MAXN];
void prime(){
phi[1]=1;
for(int i=2;i<=n;++i){
if(!np[i]){
p[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot&&i*p[j]<=n;++j){
np[i*p[j]]=true;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
}
ll ksm(ll a,ll b,ll p){
ll ans=1;
a%=p;
while(b){
if(b&1ll){
ans=(ans*a)%p;
}
a=(a*a)%p;
b>>=1;
}
return ans;
}
ll solve(ll p){
return p==1?0:ksm(2,solve(phi[p])+phi[p],p);
}
int main(){
ios::sync_with_stdio(false);
prime();
ll t;
cin>>t;
while(t--){
ll p;
cin>>p;
cout<<solve(p)<<endl;
}
return 0;
}