【OI】剩余(进阶)

前言

关于数论剩余部分的进阶内容。

欧拉定理

我们已经定义了模 \(m\) 意义下的四则运算,其中加、减、乘非常好理解,对于除法,只需乘上除数的逆元即可。而在实数域,还有幂的定义,接下来我们将探讨模意义下幂的性质。

欧拉定理:对于 \(a,m \in \Z,\gcd(a,m)=1\),有 \(a^{\varphi(m)} \equiv 1 \pmod m\)

证明:

构造集合 \(R=\{r \in \N \mid \gcd(r,m)=1\}\),显然 \(R\)\(m\) 的一个缩系,且 \(|R|=\varphi(m)\)。再构造集合 \(S=\{ar\mid r \in R\}\),根据缩系的性质,\(S\) 也为 \(m\) 的一个缩系。

于是有:

\[\prod_{ar \in S} ar=a^{\varphi(m)}\prod_{r \in R} r \equiv \prod_{r \in R} r \pmod m \]

两边约掉,即得:\(a^{\varphi(m)} \equiv 1 \pmod m\)

费马小定理

不难发现,当 \(m\) 为素数时,\(\varphi(m)=m-1\),于是得出:\(a^{p-1} \equiv 1 \pmod p\),这就是费马小定理

先看一个例子:模意义下从 \(0\) 开始每次加上 \(a\),最终显然会形成一个环,这个环上的数两两不同余,它的长度是 \(\frac{m}{\gcd(a,m)}\)

根据欧拉定理,我们会发现这样一个事实,对于 \(\gcd(a,m)=1\),我们从 \(1\) 开始乘 \(a\),也总会形成一个循环。

但是这个环长一定是欧拉定理中的 \(\varphi(m)\) 吗?并不一定是,自己手模几个数就会发现了,例如 \(a=4,m=5\),它的环长是 \(2 \neq \varphi(5)=4\),同样的,这个最小环上的数两两不同余。

于是定义,\(\gcd(a,m)=1,a^k \equiv 1 \pmod m\) 的最小的 \(k\) 称为 \(a\)\(m\),记为 \(\delta_m(a)\),也可以像 A_zjzj 一样记成 \(|a|\)

  • 定理 1\(\delta_m(a) \mid \varphi(m)\)

证明:假设定理不成立,那么则有 \(\varphi(m)=k\delta_m(a)+r,r < \delta_m(a)\),于是:

\[a^{\varphi(m)} \equiv a^{k\delta_m(a)+r} \equiv 1 \pmod m \]

\[a^r \equiv 1 \pmod m \]

但是 \(r < \delta_m(a)\),这与阶的最小性矛盾。

同理可以发现若 \(a^k \equiv 1 \pmod m\),则有 \(\delta_m(a) \mid k\)。根据这个定理,我们可以求出任意 \(a\) 的阶:

\(a\) 的阶为 \(k\),刚开始我们令 \(k=\varphi(m)\),并对 \(\varphi(m)\) 进行质因数分解,这个步骤用 pollard-rho 可以优化到 \(O(m^{0.25})\)。然后枚举所有素因子 \(p\),如果当前 \(a^{\frac{k}{p}} \equiv 1 \pmod m\),根据定理,\(\delta_m(a) \mid \frac{k}{p}\),把 \(k\) 除掉 \(p\) 后继续进行这个步骤。

求单个阶的时间复杂度是 \(O(\log^2 m)\)

  • 定理 2\(\delta_m(a^k) = \frac{\delta_m(a)}{\gcd(\delta_m(a),k)}\)

证明:不难发现这个 \(a^k\) 也是最小环上的一个元素,这就相当于每次在环上走 \(k\) 步,走最少的次数回到原点。这个值为 \(\frac{\delta_m(a)}{\gcd(\delta_m(a),k)}\)

  • 定理 3\(\frac{{\operatorname{lcm}(\delta_m(a),\delta_m(b))}}{{\gcd(\delta_m(a),\delta_m(b))}} \mid \delta_m(ab) \mid \operatorname{lcm}(\delta_m(a),\delta_m(b))\)

证明

\[(ab)^{\operatorname{lcm}(\delta_m(a),\delta_m(b))}=a^{\operatorname{lcm}(\delta_m(a),\delta_m(b))}b^{\operatorname{lcm}(\delta_m(a),\delta_m(b))} \]

由于 \(\delta_m(a),\delta_m(b) \mid {\operatorname{lcm}(\delta_m(a),\delta_m(b))}\),于是上面式子同余 \(1\),根据定理 1,\(\delta_m(ab) \mid {\operatorname{lcm}(\delta_m(a),\delta_m(b))}\),右边得证。

左边部分,由于:

\[1 \equiv (ab)^{\delta_m(ab)\delta(b)} \equiv a^{\delta_m(ab)\delta_m(b)} \pmod m \]

根据定理 1,\(\delta_m(a) \mid \delta_m(ab)\delta_m(b)\),两边除掉 \(\gcd(\delta_m(a),\delta_m(b))\),则有:

\[\frac{\delta_m(a)}{\gcd(\delta_m(a),\delta_m(b))} | \frac{\delta_m(ab)\delta(b)}{\gcd(\delta_m(a),\delta_m(b))} \]

同理:

\[\frac{\delta_m(b)}{\gcd(\delta_m(a),\delta_m(b))} | \frac{\delta_m(ab)\delta(a)}{\gcd(\delta_m(a),\delta_m(b))} \]

由于 \(\gcd(\frac{\delta_m(a)}{\gcd(\delta_m(a),\delta_m(b))},\frac{\delta_m(b)}{\gcd(\delta_m(a),\delta_m(b))})=1\),所以这两个式子都整除 \(\delta_m(ab)\) 且它们的乘积也整除 \(\delta_m(ab)\)

\(\frac{\delta_m(a)\delta_m(b)}{\gcd^2(\delta_m(a),\delta_m(b))}=\frac{{\operatorname{lcm}(\delta_m(a),\delta_m(b))}}{{\gcd(\delta_m(a),\delta_m(b))}}\),于是左边也得证。

注意到当 \(\gcd(\delta_m(a),\delta_m(b))=1\) 时,\(\frac{{\operatorname{lcm}(\delta_m(a),\delta_m(b))}}{{\gcd(\delta_m(a),\delta_m(b))}}=\operatorname{lcm}(\delta_m(a),\delta_m(b))=\delta_m(a)\delta_m(b)\)。根据我们刚才的推论,有 \(\delta_m(ab)=\delta_m(a)\delta_m(b)\)

原根

而有一类数 \(g\),它的阶就等于 \(\varphi(m)\),称 \(g\) 为模 \(m\)原根,显然 \(g\) 的幂环刚好构成了 \(m\) 的整个缩系。

  • 原根判定定理:设 \(\varphi(m)\) 的所有素因子构成的集合为 \(P\),若 \(\forall p \in P\),都有 \(g^{\frac{\varphi(m)}{p}} \not\equiv 1 \pmod m\),则 \(g\)\(m\) 的一个原根。

证明:其实上述的步骤是在说明 \(\delta_m(g)=\varphi(m)\),而不是 \(\varphi(m)\) 的任何一个因子。显然这个算法的时间复杂度也是 \(O(\log^2 m)\)

  • 原根个数:模 \(m\) 的原根个数是 \(\varphi(\varphi(m))\)

证明:设 \(m\) 的最小原根为 \(g\),显然所有原根都能用 \(g\) 的幂表示,这是因为 \(g\) 的幂已经构成了 \(m\) 的缩系。而根据阶的定理 2,\(\delta_m(g^k)=\frac{\delta_m(g)}{\gcd(\delta_m(g),k)}=\frac{\varphi(m)}{\gcd(\varphi(m),k)}\),又因为 \(\delta_m(g^k)=\varphi(m)\),于是有 \(\gcd(\varphi(m),k)=1\),故 \(k\) 的个数为 \(\varphi(\varphi(m))\)

  • 原根存在定理:模 \(m\) 存在原根的充要条件为 \(m=2,4,p^e,2p^e\),其中 \(p\) 为奇素数,\(e \in \N_+\)(不会)

那么如何求解一个模的原根呢?王元证明了任意素数 \(p\) 的最小原根上界为 \(O(p^{\frac{1}{4}+\epsilon})\),后人证明这个上界对所有形式的模都成立。

于是我们可以从小到大枚举 \(g\),然后用原根判定定理,总时间复杂度为 \(O(m^\frac{1}{4}\log^2 m)\)

例题

\[x^k \equiv y \pmod m \]

注意 \(m\) 为奇素数的幂,所以它一定有原根,令其为 \(g\),由于 \(x,y \bot m\),所以一定有 \(g^a \equiv x \pmod m\)\(g^b \equiv y \pmod m\)

可以把式子化为 \(g^{ak} \equiv g^b \pmod m\),相当于 \(ak \equiv b \pmod {\varphi(m)}\),根据裴蜀定理,化为 \(\gcd(a,\varphi(m)) \mid b\),等价于 \(\gcd(a,\varphi(m)) \mid \gcd(b,\varphi(m))\)

由于 \(\delta_m(x)=\frac{\varphi(m)}{\gcd(a,\varphi(m))},\delta_m(y)=\frac{\varphi(m)}{\gcd(b,\varphi(m))}\),上式实则等价于 \(\delta_m(y) \mid \delta_m(x)\),于是我们运用试除法求阶即可。

但是数据范围实在有点大,需要用 pollard-rho 分解质因数。

离散对数

既然有了幂和指数,那么按理来说也应该有对数的定义。

取一个有原根的模 \(m\),取一个原根 \(g\),我们知道原根的幂可以生成 \(m\) 的整个缩系,那么对于 \(\gcd(a,m)=1\),必然存在 \(g^k \equiv a \pmod m\)

我们记 \(k= \operatorname{ind}_g a\),称为模 \(m\) 意义下以 \(g\) 为底 \(a\) 的离散对数。

一般地,离散对数有与对数相似的性质,对于 \(a,b \bot m\),有:

  • \(\operatorname{ind}_g (ab) \equiv \operatorname{ind}_g a+\operatorname{ind}_g b \pmod {\varphi(m)}\)
  • \(g_1\) 也是 \(m\) 的原根,有 \(\operatorname{ind}_g a \equiv \operatorname{ind}_{g_1} a \times \operatorname{ind}_g g_1 \pmod {\varphi(m)}\)
  • \(a \equiv b\),则 \(\operatorname{ind}_g a=\operatorname{ind}_g b\)

BSGS

那么如果不以原根为底呢?看这个问题:

对于 \(a,b\bot m\),求满足 \(a^x \equiv b \pmod m\) 的最小的 \(x\),或者报告这样的 \(x\) 不存在。

前面我们已经给出了有解的充要条件:\(\delta_m(a) \mid \delta_m(b)\),但是我们并不知道如何求出这个 \(x\)。事实上目前还没有多项式时间的算法求解离散对数,下面我们介绍一种思想,名叫 BSGS(Baby Step Giant Step,大步小步)

\(p=\lceil\sqrt{m}\rceil\),假设 \(x=Ap-B,A,B \in [0,p]\),则有 \(a^{Ap} \equiv ba^B \equiv m\),我们可以枚举 \(B\) 的值,然后把它们用 hash/map 存下来,然后再枚举 \(A\) 去 hash/map 里查找,匹配到相同值则有 \(x=Ap-B\),显然时间复杂度为 \(O(\sqrt m)\)

这个算法要求 \(a \bot m\),这是因为需要存在逆元第二步的变换才成立。

如果用 map 会多一个 log,可以考虑使用 unordered_map,然后记得把下面代码里的唐诗 qpow 给改掉。

例题

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=long double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
map<int, int> mp;
int p,a,b;
inline int qpow(int x, int y, int p){
	int ans=1;
	while(y>0){
		if(y&1) ans=ans*x%p;
		x=x*x%p,y>>=1;
	}
	return ans%p;
}
inline int bsgs(){
	int t=sqrt(p)+1;
	for(rg int i=0;i<t;i++) mp[b*qpow(a,i,p)%p]=i;
	a=qpow(a,t,p)%p;
	if(!a) return (!b)?1:-1;
	for(rg int i=1;i<=t;i++){
		int tmp=qpow(a,i,p)%p;
		int B=(mp.find(tmp)==mp.end())?-1:mp[tmp];
		if(~B&&i*t-B>=0) return i*t-B;
	}
	return -1;
}
signed main(){
	p=read(),a=read(),b=read();
	int ans=bsgs();
	if(ans==-1) puts("no solution");
	else write(ans);
	return 0;
}

exBSGS

如果不满足 \(a,b \bot m\) 呢?

\[a^x \equiv b \pmod m \]

这时候我们不妨令 \(g=\gcd(a,m)\),然后原式就变成这样:

\[\frac{a}{g} a^{x-1} \equiv \frac{b}{g} \pmod {\frac{m}{g}} \]

但是 \(\gcd(a,\frac{m}{g})\) 不一定为 \(1\),于是我们可以继续除 \(\gcd\),如果 \(b\) 不能被整除显然是无解的。

最后式子会变成这样:

\[\frac{a^k}{\prod g} a^{x-k} \equiv \frac{b}{\prod g} \pmod {\frac{m}{\prod g}} \]

显然左边这个系数是有逆元的,把它移到右边,然后就是一个普通 BSGS 了。

例题

代码暂时懒得写了,有空再补吧。

赛事级例题

非常简单的题,分别是快速幂,逆元与离散对数,直接套板子就行。

值得注意的是,exBSGS 板子也要用到这三个东西。

后面忘了。

\[End \]

posted @ 2026-04-02 20:46  bbbzzx  阅读(11)  评论(0)    收藏  举报