数学基础-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\) 的质因子构成。

  1. \(p_j\)

  2. \(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\)

  1. \(a\) 可以整除 \(b\) 的时候,则 \(g=b\)

  2. \(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(b,a\bmod b)=bx+(a\bmod b)y \]

因为 \(\gcd(a,b)=\gcd(b,a\bmod b)\)。所以:

\[\gcd(a,b)=bx+(a\bmod b)y \]

只需要把 \(a,b\) 提取出来就可以。

众所周知,\(a\bmod b=a-\lfloor\frac{a}{b}\rfloor\times b\),代入得:

\[\gcd(a,b)=bx+(a-\lfloor\frac{a}{b}\rfloor\times b)y \]

展开:

\[\gcd(a,b)=bx+ay-\lfloor\frac{a}{b}\rfloor\times by \]

合并:

\[\gcd(a,b)=ay+b(x-\lfloor\frac{a}{b}\rfloor\times y) \]

此时,令 \(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\) 的简化剩余系。

由此可以得到以下结论:

\[\prod_{i=1}^{\varphi(n)}p_i\equiv \prod_{i=1}^{\varphi(n)}ap_i\pmod n \]

\[\prod_{i=1}^{\varphi(n)}p_i\equiv \prod_{i=1}^{\varphi(n)}a\times \prod_{i=1}^{\varphi(n)}p_i\pmod n \]

\[\prod_{i=1}^{\varphi(n)}a\equiv 1\pmod n \]

\[a^{\varphi(n)}=1\pmod 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))\),则有

\[a^b\equiv a^{k\times \varphi(n)}\times a^r\equiv ((a^{\varphi(n)})^k)\times a^r\equiv a^r \]

我们可以把 \(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)}\) 即可。

无解情况用裴蜀定理判定。

线性同余方程组

求解形如:

\[\begin{cases}x\equiv b_1\pmod{a_1}\\x\equiv b_2\pmod{a_2}\\\dots\\x\equiv b_n\pmod{a_n}\end{cases} \]

方程组的方法。

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;
}

高次剩余

这个是真不会

posted @ 2024-08-30 21:43  zuoqingyuan111  阅读(9)  评论(0编辑  收藏  举报