数学基础-first
数学不如小学生。
数论
就是研究质数合数的一些东西,小学讲过。
质数判断
就是判断一个数是否是质数。
试除法
非常简单,如果一个数 \(x\) 不是质数,那么它一定是合数;如果 \(x\) 是合数,那么他至少有三个因子,枚举 \([2,x-1]\) 中的数,依次判断是否可以整除 \(x\)。
时间复杂度 \(O(n)\),有点低效。
假定存在 \(a\times b=x\),且 \(a\le b\),那么一定会有 \(a\le \sqrt{x}\)。所以说,如果我们只枚举 \([2,\sqrt{x}]\) 中的数,同样可以准确的判断其是否是质数
时间复杂度 \(O(\sqrt{n})\),已经相当不错的效率了。
点击查看代码
bool check(int p){
if(p==1)return 0;
if(p==2)return 1;
for(int i=2;i*i<=p;i++){
if(p%i==0)return 0;
}
return 1;
}
特别通用的方法,也是唯一一个正确性得到保障的大众做法了。
Miller–Rabin
建议先学习“费马小定理”。
虽然试除法时间复杂度 \(O(\sqrt n)\),但如果 \(n\le 10^{18}\),又应当如果做?
众所周知:当 \(a,p\) 互质且 \(p\) 为质数,则 \(a^{p-1}\equiv 1\pmod p\)。
值得一提的是,费马小定理的逆定理(当 \(a,p\) 互质且 \(a^{p-1}\equiv 1\pmod p\),则 \(p\) 为质数)是不一定成立的。
也就是说,如果我们随机一个数 \(a(2\le a<p)\),然后通过判断 \(a^{p-1}\equiv 1\pmod p\) 是否成立来反映 \(p\) 为质数,这种做法是不正确的,会有一定的概率误判。
不过误判的概率并不是很高,肯定小于 \(\frac{1}{2}\)。那么我们可以多判几次,一旦有一次不成立就直接代表这个数是合数,大概判断 \(30\) 多次,如果每一次都使得等式是成立的,那么这个数大概率就是质数。
快速幂都会写吧...
点击查看代码
typedef long long ll;
ll ksm(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans;
}
bool check(int p){
if(p==1)return 0;
if(p==2)return 1;
srand(time(0));
for(int i=1;i<=30;i++){
ll a=rand()%p;
if(ksm(a,p-1,p)!=1)return 0;
}
return 1;
}
质数筛
就是快速筛出 \([1,n]\) 的质数,也会有一些奇特的用法,例如筛出每个数的质因子个数,欧拉函数,莫比乌斯函数之类的。不过我们现在还是只探讨如果快速筛出质数。
埃氏筛
好用。
一个很简单的思想,如果一个数 \(p\) 是质数,那么 \(2p,3p,\dots,kp(kp\le n)\) 均为合数,我们把这些数打上标记。
我们从 \(2\) 向 \(n\) 扫,遇到一个没打标记的数 \(x\)(它不是 \([2,x-1]\) 中任何数的倍数),则说明他是质数。
遇到一个质数,就将它的所有倍数标记为合数,遇到一个合数,则跳过,重复执行直到扫到 \(n\)。
时间复杂度 \(O(n\log\log n)\),不知道怎么来的。
点击查看代码
void init(int n){
vis[1]=1;
for(int i=2;i<=n;i++){
if(vis[i])continue;
for(int j=2;i*j<=n;j++){
vis[i*j]=1;
}
}
return;
}
欧拉筛
也叫线性筛(xxs),其实就是改进了上面的算法。
我们发现每个合数在上面的算法中会被多次打上标记,例如 \(30=2\times 3\times 5\),当我们扫到 \(2,3,5\) 时,都会把 \(30\) 打上一次标记。
现在我们希望做到线性,即每个数只会通过另一个唯一的数打上标记。
假设 \(p\) 为 \(x\) 的最小质因子,那么我们希望 \(x\) 会被 \(\frac{x}{p}\) 筛到,怎么做到呢?直接看代码吧。
点击查看代码
void init(int n){
cnt=0;
vis[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i])p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;j++){
vis[i*p[j]]=1;
if(i%p[j]==0)break;
}
}
return;
}
j<=cnt&&i*p[j]<=n
应当很好懂,主要是 if(i%p[j]==0)break;
这一句比较难解释。
简单来讲就是,我们希望 \(i\times p_j\) 的最小质因子为 \(p_j\),且它只会通过 \(i\) 被打上标记,我们思考 \(i\times p_j\) 的质因子构成。
-
\(p_j\)
-
\(i\) 的质因子
因为我们从小到大枚举 \(p_j\),因此 if(i%p[j]==0)break;
从来没成立过时,\(p_j\) 一定小于 \(i\) 的最小质因子,因此一定可以保证 \(i\times p_j\) 的最小质因子为 \(p_j\)。
当 if(i%p[j]==0)break;
成立后,对于任意 \(k>j\),都满足 \(i\times p_k\) 的最小质因子为 \(i\) 的最小质因子。
这东西很有用,筛出每个数的最小质因子后,支持快速的质因数分解(前提是值域小)。
时间复杂度 \(O(n)\)。
质因数分解
唯一分解定理:对于任意正整数 \(x\) 均可以表示成 \(\prod{p_i^{c_i}}\) 的形式。其中 \(p_i\) 为质数,\(c_i\) 为正整数。
质因数分解就是快速求出 \(p_i\) 和 \(c_i\)。
试除法
仿照判断质数的方法,我们枚举因子,可以做到 \(O(\sqrt{n})\)。
点击查看代码
void f(int n){
for(int i=2;i*i<=n;i++){
while(n%i==0)n/=i,p[++cnt]=i;
}
if(n>1)p[++cnt]=n;
return;
}
并没有求出每个质因子的指数,而是将其表现成了若干个质因子相乘的形式。
注意最后一步特判 \(n>1\) 的情况,这代表此时的 \(n\) 为一个大质数。
Pollard Rho 算法
不会。
最大公约数
对于一对数 \((a,b)\),如果存在 \(d\) 同时为 \(a,b\) 的因数,则称 \(d\) 为 \(a,b\) 的公因数,其中,\(a,b\) 最大的公因数称为 \(a,b\) 的最大公因数,通常用 \(\gcd(a,b)\) 表示。
本节主要解决和最大公约数相关问题。
欧几里得算法
首先有这么一个人尽皆知的结论:
\(\gcd(a,b)=\begin{cases}a&b=0\\\gcd(b,a\bmod b)&\text{otherwise}\\\end{cases}\)
如何证明?
假设 \(a,b\) 的最大公约数为 \(g\)。
-
当 \(a\) 可以整除 \(b\) 的时候,则 \(g=b\)。
-
当 \(a\) 无法整除 \(b\) 的时候,我们只知道 \(a\) 可以整除 \(g\),\(b\) 也可以整除 \(g\),\(2b,3b,\dots,kb\) 都可以整除 \(g\)。
因此,对于小于 \(a\) 的最大的 \(kb\)(\((k-1)b<a\le kb\))。也可以被 \(g\) 整除,所以说 \(a-kb\) 也可以被 \(g\) 整除 。
我们缩小了范围,但并没有丢失答案,可以得到,此时 \(\gcd(a,b)=\gcd(b,a-kb)\),\(a-kb\) 也可以写成 \(a\bmod b\)。
我们重新返回算法本身:
- 对于 \(a>b\) 的情况:
\(\gcd(a,b)=\gcd(b,a\bmod b)=\gcd(b,a)\),可以转化为 \(a<b\) 的情况。
- 对于 \(a<b\) 的情况。
如果 \(b=0\),我们倒退回上一步,此时代表 \(a\bmod b=0\),即当 \(a\) 可以整除 \(b\) 的时候,此时的最大公约数即为这一步的 \(b\),下一步的 \(a\)。
否则就是上边的第二种情况。
点击查看代码
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
时间复杂度 \(O(\log n)\)
裴蜀定理
有这么一个不人尽皆知的结论(可能只有我当时不知道)。
对于一对正整数 \(a,b\),一定存在一组 \(x,y\),满足 \(ax+by=\gcd(x,y)\)。
如何证明?通常结合欧几里得算法,采用数学归纳法。
回到欧几里得算法最后一步,当 \(b=0\) 时,\(\gcd(a,b)=a\),此时 \(x=1,y=0\) 就是一组可以构造的合法解。
对于算法过程中任意一步 \(\gcd(a,b)\),我们假设其之后的每一步都有解。
因为下一步有解,所以存在
因为 \(\gcd(a,b)=\gcd(b,a\bmod b)\)。所以:
只需要把 \(a,b\) 提取出来就可以。
众所周知,\(a\bmod b=a-\lfloor\frac{a}{b}\rfloor\times b\),代入得:
展开:
合并:
此时,令 \(x'=y,y'=x-\lfloor\frac{a}{b}\rfloor\times y\),则满足 \(ax'+by'=\gcd(a,b)\)。
这么一直转移,最后即可满足构造一组可行解。
根据的等式的基本性质,我们可以将结论继续推广:
对于二元一次不定方程 \(ax+by=c\),如果满足 \(c\) 能整除 \(\gcd(a,b)\),则方程有解。
对于怎么构造解,就超出目前的范围了
最小公倍数
对于一对数 \((a,b)\),如果存在 \(d\) 同时为 \(a,b\) 的倍数,则称 \(d\) 为 \(a,b\) 的公倍数,其中,\(a,b\) 最小的公倍数称为 \(a,b\) 的最小公倍数,通常用 \(\text{lcm}(a,b)\) 表示。
根据定义可得,对于一对数 \(a,b\),满足 \(a\times b=\gcd(a,b)\times \text{lcm}(a,b)\)。
积性函数
不会,不过下面可能会用到,所以介绍几个我会的。
- \(\varphi(n)\):表示 \(1\sim n\) 中和 \(n\) 互质的数的个数。
同余
同余的定义和基本性质
比较简单的东西(单单指这一部分)。
如果 \(a\) 除 \(m\) 的余数为 \(b\),则写作 \(a\equiv b\pmod{m}\),读作“\(a\) 同余 \(b\) 模 \(m\)”,其中 \(m\ne 0\)(下文默认这个条件)。
同余满足两条基本性质:
-
如果 \(a\equiv b\pmod{m}\),则 \(a+c\equiv b+c\pmod{m}\)。
-
如果 \(a\equiv b\pmod{m}\),则 \(ac\equiv bc\pmod{m}\)。
然后记一些比较杂的东西。
显然,对于任意 \(a,m\),\(a\bmod m\) 有 \(m\) 种不同的结果,这 \(m\) 个结果构成的集合可以表示为 \(\{0,1,\dots,m-2,m-1\}\),记作 \(m\) 的完全剩余系。
其中,如果我们只挑选出和 \(m\) 互质的数新组成一个集合,就得到了 \(m\) 的简化剩余系。
简化剩余系有一个性质,对于任意和 \(n\) 互质的两数 \(a,b\),都保证 \(ab\equiv n\) 在 \(n\) 的简化剩余系中,这种性质称其为封闭性。
然后是一些代码编写中常用的东西。
比如 \((a+b)\bmod m=(a\bmod m)+(b\bmod m)\),这种方法同样对减法和乘法适用,这样我们就可以在计算答案的过程中不断取模。
特殊的,对于任意负数模正数均为一个整数,不过 C++ 自带的取余运算 %
在解决这种情况时会返回一个负数,因此对于一个负数,常用的取模方法是 \((c\bmod m+m)\bmod m\)。
同余定理
一些常用的定理。
欧拉定理
对于互质的一对数 \(a,n\),存在 \(a^{\varphi(n)}\equiv 1\pmod{n}\)。
这个定理很好证明,前提是掌握了简化剩余系的封闭性以及同余的性质。
假设 \(n\) 的简化剩余系为 \(\{p_1,p_2,\dots,p_{\varphi(n)}\}\)。
此时简化剩余系内的所有数均和 \(n\) 互质(简化剩余系定义),且 \(a\) 和 \(n\) 互质,根据简化剩余系的封闭性,\(\forall i\le \varphi(n)\),都存在 \(ap_i\bmod n\) 在 \(n\) 的简化剩余系内。
我们观察 \(\{ap_1,ap_2,\dots,ap_{\varphi(n)}\}\),发现这些元素没有一个重复,这条结论很好证明,假设这个集合中存在两个元素 \(ap_{i}\equiv ap_{j}\pmod n\),想要出现这种情况,当且仅当 \(p_i\equiv p_j\pmod n\),这显然不符合简化剩余系定义,所以原结论成立。
因为这个集合中的每个数都在 \(n\) 的简化剩余系中,且 \(\varphi(n)\) 的元素互不相同,因此可以说明新构成的集合同样是 \(n\) 的简化剩余系。
由此可以得到以下结论:
扩展欧拉定理
欧拉定理推论:对于互质的一对数 \(a,n\),对于任意 \(b\),存在 \(a^b\equiv a^{b\bmod \varphi(n)}\pmod{n}\)。
假设 \(b=k\times \varphi(n)+r(0\le r<\varphi(n))\),则有
我们可以把 \(r\) 写成 \(b\bmod \varphi(n)\) 的形式。
不过哪怕 \(a,n\) 不互质,也存在另一条定理。
欧拉拓展定理:对于一对正整数 \(a,b,n\),满足:
\(a^b=\begin{cases}a^{b\bmod \varphi(n)}&\gcd(a,n)=1\\a^b&\gcd(a,n)\ne 1,b<\varphi(n)\\a^{b\bmod \varphi(n)+\varphi(n)}&\gcd(a,n)\ne 1,b\ge \varphi(n)\end{cases}\)
证明比较复杂,就不证明了。
可以用来求 \(k=c^{c^{c^{\dots}}}\) 中 \(k\) 的值。
费马小定理
对于任意质数 \(p\),且 \(a,p\) 互质,则满足 \(a^p\equiv p\pmod p\)。
首先我们要知道,对于任何一个质数 \(p\),都有 \(\varphi(p)=p-1\),即除了他本身以外的所有数都和他互质。
这样我们就可以套用欧拉定理,可得 \(a^{\varphi(p)}\equiv 1\pmod p\),即 \(a^{p-1}\equiv 1\pmod p\)。
变形得,\(a^p\equiv p\pmod p\)。
线性同余方程
解决关于 \(x\) 的方程 \(ax\equiv b\pmod p\) 的解,其中 \(a,b\) 均为常数。
乘法逆元
相信很多人都会想到,令 \(x=\frac{b}{a} \bmod p\) 的值,但是模意义下的除法该如何下手?
有这么一个叫乘法逆元的东西,通俗的来讲,如果 \(c\) 为 \(a\) 模 \(p\) 的乘法逆元,则有 \(c\equiv \frac{1}{a}\pmod p\),值得一提的是,不一定每一个数都有乘法逆元,这很大程度上取决于模数。
因为乘法逆元的运用范围通常是模数为质数的情况,所以下文我们的讨论均已 \(p\) 为质数展开。
移项可得 \(ac\equiv 1 \pmod p\),根据费马小定理,可知 \(a^{p-1}\equiv 1\pmod p\),所以可以得到:\(ac\equiv a^{p-1}\pmod p\)。
两边同除 \(a\) 得:\(c=a^{p-2}\bmod p\)。
通过快速幂即可快速求解。
扩展欧几里得算法
如果 \(ax\equiv b\pmod p\),那么相当于 \(ax=yp+b\),移项得 \(ax-yp=b\),等价于求出一组 \(x,y\) 满足 \(ax+py=b\)。
运用扩展欧几里得定理,可以求出 \(ax+py=\gcd(a,p)\) 的一组解,此时我们在将等式两边同乘 \(\frac{b}{\gcd(a,p)}\) 即可。
无解情况用裴蜀定理判定。
线性同余方程组
求解形如:
方程组的方法。
CRT
没啥用,鸽。
exCRT
很好理解,效率较高,应用范围更广,代码不长,全面薄纱 CRT。
exCRT 的核心在于合并相邻两个线性同余方程的解。
假如说我们求出了前 \(k-1\) 个方程的解 \(x\),如果在 \(x\) 上略作修改使其成为前 \(k\) 个方程的解呢?
令 \(l=\text{lcm}(a_1,a_2,\dots,a_{k-1})\),则对于任意正整数 \(t\),\(x+t\times l\) 均为前 \(k-1\) 个方程的解。我们所要做的,就是同时求出最小的 \(x+t\times l\) 满足 \(x+t\times l\equiv b_k\pmod{a_{k}}\)。其中只有 \(t\) 是未知数。
令 \(a=l,x=t,b=b_k-x\),则原方程等价于求出 \(ax\equiv b\pmod{a_k}\),可以用扩展欧几里得实现,并用裴蜀定理判定无解。
求出 \(t\) 后,令 \(x\gets x+t\times l,l\gets \text{lcm}(l,a_k),k\gets k+1\)。直至 \(k=n\)。
记得在更新 \(l\) 后用 \(l\) 取模 \(x\),放置溢出,如果数据范围很大可能会用到 int128
或龟速乘。
点击查看代码
int128 exCRT(){
int128 x=d[1],lcm=m[1],a,b,c;
for(int i=2;i<=n;i++){
a=lcm,c=Mod(d[i]-x,m[i]);
solve(a,b,c,m[i]);//求解 ab mod m[i] = c mod m[i] 的 b
x=(x+b*lcm);
lcm=(lcm/exgcd(lcm,m[i],a,b)*m[i]);
x%=lcm;
}
return x;
}
高次同余方程
求解形如 \(a^x\equiv b\pmod{p}\) 或 \(x^a\equiv b\pmod{p}\) 的方程。
BSGS
用于求解前者,要求 \(p\) 为质数。
显然,\(x\in[0,p-1]\),否则可以用 \(x\bmod p\) 来代替(费马小定理可证)。
我们设定一个阈值 \(t\),则对于 \(x\) 可以表示成 \(i\times t-j\) 的形式。我们可以花费 \(O(t)\) 的时间复杂度求出所有的 \(a^j\),花费 \(O(\frac{p}{t})\) 的时间复杂度内求出所有 \(a^{i\times t}\)。
显然,如果存在 \(a^{i\times t-j}\equiv b\pmod p\),则可以变形为 \(a^{i\times t}\equiv a^j\times b\pmod p\)。
我们在求出所有 \(a^j\) 的时候顺便求出 \(a^j\times b\bmod p\),然后存入哈希表中,在求 \(a^{i\times t}\bmod p\) 后查找是否有值和他相等,用来更新答案。
显然,设 \(t=\sqrt p\) 时最优,时间复杂度 \(O(\sqrt p)\)。不过由于我写法的问题,时间复杂度为 \(O(\sqrt p\log p)\)。
点击查看代码
map<ll,ll>Hash;
ll p,a,b;
ll ksm(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans;
}
ll BSGS(){
Hash.clear();
ll t=1ll*ceil(sqrt(p));
for(int i=0;i<t;i++){
ll val=b*ksm(a,i,p)%p;
Hash[val]=i;
}
for(int i=1;i<=t;i++){
ll val=ksm(a,i*t,p);
if(Hash.count(val))return i*t-Hash[val];
}
return -1;
}
高次剩余
这个是真不会