整除和同余相关理论

数论专题笔记(1)——同余,整除,欧拉定理,中国剩余定理 一. 同余定义:给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系。 显然,有如下事实 (1) 若a≡0(mod m) 则 m | a; (2) a≡b(mod m)等价于a与b分别用m去除,余数相同. 性质: (1) 反身性 a ≡ a(modm) 显然成立; (2) 对称性 a ≡ b(modm) <=> b ≡ a(modm) (3) 传递性 若 a ≡ b(mod 阅读详情

下面的所有知识均属数论范畴。


LucasLucasLucas 定理

当 ppp 为质数时有:

Cnm mod p=Cn/pm/p∗Cn  mod  pm  mod  p mod p C_n^m \bmod p= C_{n / p}^{m / p} * C_{n~\bmod~p}^{m~\bmod~p} \bmod p Cnm​modp=Cn/pm/p​∗Cn mod pm mod p​modp

代码就不贴了 (实际上是懒)。


如果 ppp 不为质数呢?

那么我们先用唯一分解定理将 ppp 分解:
p=∑ki∈Primekiαi p = \sum_{k_i \in Prime} k_i^{\alpha_i} p=ki​∈Prime∑​kiαi​​
然后把原式拆分为若干个同余方程,最后用中国剩余定理合并即可。

现在我们讨论如何求 Cnm mod kiαiC_n^m \bmod k_i^{\alpha_i}Cnm​modkiαi​​,而 Cnm=n!m!(n−m)!C_n^m = \frac{n!}{m!(n - m)!}Cnm​=m!(n−m)!n!​,所以问题本质上是求阶乘的取模。

设我们要求的是 P! mod  kiαiP! \bmod~k_i^{\alpha_i}P!mod kiαi​​,那么我们可以把 P!P!P! 包含的所有 kik_iki​ 质因子取出,由于剩下的数与模数互质,所以我们就可以直接求逆元了。

所以最终要求的问题是求:

n!kiβ1m!kiβ2⋅(n−m)!kiβ3⋅kiβ1−β2−β3 mod kiαi \frac{\frac{n!}{k_i^{\beta_1}}}{\frac{m!}{k_i^{\beta_2}} \cdot \frac{(n - m)!}{k_i^{\beta_3}}} \cdot k_i^{\beta_1 - \beta_2 - \beta_3}\bmod k_i^{\alpha_i} kiβ2​​m!​⋅kiβ3​​(n−m)!​kiβ1​​n!​​⋅kiβ1​−β2​−β3​​modkiαi​​

不过我们还是先从研究 P! mod  kiαiP! \bmod~k_i^{\alpha_i}P!mod kiαi​​ 开始。

同 OI−wikiOI-wikiOI−wiki,我们用 22! mod 3222! \bmod 3^222!mod32 举例。

我们知道:
22!=1×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×19×20×21×22. 22! = 1\times2\times3\times4\times5\times6\times7\times8\times9\times10\times11\times12\times13\times14\times15\times16\times17\times18\times19\times20\times21\times22. 22!=1×2×3×4×5×6×7×8×9×10×11×12×13×14×15×16×17×18×19×20×21×22.

我们把 333 的倍数提出来,化简一下有:
22!=37×(1×2×3×4×5×6×7)×(1×2×4×5×7×8×10×11×13×14×16×17×19×20×22) 22! = 3^7\times(1\times2\times3\times4\times5\times6\times7) \times(1\times2\times4\times5\times7\times8\times10\times11\times13\times14\times16\times17\times19\times20\times22) 22!=37×(1×2×3×4×5×6×7)×(1×2×4×5×7×8×10×11×13×14×16×17×19×20×22)

第一个部分是 kik_iki​ 的 ⌊Pki⌋\lfloor\frac{P}{k_i}\rfloor⌊ki​P​⌋ 次幂,这个部分只需要记录指数,不需要求具体的值;

第二个部分是 ⌊Pki⌋!\lfloor\frac{P}{k_i}\rfloor!⌊ki​P​⌋!,可以递归求解;

第三个部分是 1×2×4×5×7×8×10×11×13×14×16×171\times2\times4\times5\times7\times8\times10\times11\times13\times14\times16\times171×2×4×5×7×8×10×11×13×14×16×17,仔细观察可以发现:
1×2×4×5×7×8≡10×11×13×14×16×17 ( mod  kiαi) 1\times2\times4\times5\times7\times8 \equiv10\times11\times13\times14\times16\times17~(\bmod~k_i^{\alpha_i}) 1×2×4×5×7×8≡10×11×13×14×16×17 (mod kiαi​​)
形式化地,有:(t∈Z+t \in Z_+t∈Z+​)
∏i=1,gcd⁡(i,ki)=1kiαii≡∏i=1,gcd⁡(i,ki)=1kiαi(i+tkiαi) ( mod kiαi) \prod_{i = 1,\gcd(i, k_i) = 1}^{k_i^{\alpha_i}} i \equiv \prod_{i = 1,\gcd(i, k_i) = 1}^{k_i^{\alpha_i}} (i + tk_i^{\alpha_i}) ~(\bmod k_i^{\alpha_i}) i=1,gcd(i,ki​)=1∏kiαi​​​i≡i=1,gcd(i,ki​)=1∏kiαi​​​(i+tkiαi​​) (modkiαi​​)
这个循环节一共循环了 ⌊Pkiαi⌋\lfloor\frac{P}{k_i^{\alpha_i}}\rfloor⌊kiαi​​P​⌋ 次,我们直接求出来之后快速幂即可。

最后是 19×20×2219 \times 20\times 2219×20×22,循环节的余项,表示出来即为:
∏i=1,gcd⁡(i,ki)=1P  mod  kiαii \prod_{i = 1,\gcd(i, k_i) = 1}^{P~\bmod~k_i^{\alpha_i}} i i=1,gcd(i,ki​)=1∏P mod kiαi​​​i
分步求解即可。

如果多测并且模数相同,可以对模数的每一个质因子 kik_iki​,求出所有 P≤kiαiP \le k_i^{\alpha_i}P≤kiαi​​ 的 ∏i=1,gcd⁡(i,ki)=1Pi\prod_{i = 1, \gcd(i, k_i) = 1}^P i∏i=1,gcd(i,ki​)=1P​i。

贴上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>

using LL = long long;
using namespace std;

LL n, m, p;

inline LL Get(LL k, LL p) { if(k >= p) k -= p; return k; }

inline void Exgcd(LL a, LL b, LL &x, LL &y)
{
	if(b == 0LL) return x = 1LL, y = 0LL, void();
	Exgcd(b, a % b, y, x);
	return y -= a / b * x, void();
}

inline LL Pow(LL a, LL b, LL p)
{
	LL res = 1LL;
	while(b)
	{
		if(b & 1) res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}

inline LL Fac(LL k, LL pi, LL pk)
{
	if(!k) return 1LL;
	LL res = 1LL;
	for(register LL i = 2LL; i <= pk; i++) if(i % pi) res = res * i % pk;
	res = Pow(res, k / pk, pk);
	for(register LL i = 2LL; i <= k % pk; i++) if(i % pi) res = res * i % pk;
	return res * Fac(k / pi, pi, pk) % pk; 
}

inline LL Inv(LL k, LL p)
{
	LL x, y; Exgcd(k, p, x, y);
	return Get(x % p + p, p);
}

inline LL CRT(LL k, LL t) { return k * (p / t) % p * Inv(p / t, t) % p; }

inline LL C(LL n, LL m, LL pi, LL pk)
{
	LL v1 = Fac(n, pi, pk), v2 = Fac(m, pi, pk), v3 = Fac(n - m, pi, pk);
	LL k = 0LL;
	for(register LL i = n; i; i /= pi) k += i / pi;
	for(register LL i = m; i; i /= pi) k -= i / pi;
	for(register LL i = n - m; i; i /= pi) k -= i / pi;
	return v1 * Inv(v2, pk) % pk * Inv(v3, pk) % pk * Pow(pi, k, pk) % pk;
}

inline LL ExLucas(LL n, LL m, LL p)
{
	LL ans = 0LL, t = p, pt = 1;
	for(register int i = 2; i * i <= p; i++) if(t % i == 0)
	{
		while(t % i == 0) pt *= i, t /= i;
		ans = Get(ans + CRT(C(n, m, i, pt), pt), p);
		pt = 1;
	}
	if(t > 1) ans = Get(ans + CRT(C(n, m, t, t), t), p);
	return ans;
}

int main()
{
	#ifdef FILE
		freopen("Input.in", "r", stdin);	
	#endif
	scanf("%lld%lld%lld", &n, &m, &p);
	printf("%lld\n", ExLucas(n, m, p));
	return 0;
}

BSGSBSGSBSGS 算法

当 a⊥pa \perp pa⊥p 时有如下算法:

设 t=⌊p⌋t = \lfloor\sqrt{p}\rfloort=⌊p​⌋,则方程可以化为:
ait−k≡b ( mod  p) a^{it - k} \equiv b ~(\bmod~p) ait−k≡b (mod p)

进一步有:(如果 a⊥ba \perp ba⊥b 不成立,这一步得出的结论不能推至上一步)

(at)i≡bak ( mod  p) (a^t)^i \equiv b a^k~(\bmod~p) (at)i≡bak (mod p)

因为 k∈[0,t)k \in [0, t)k∈[0,t),所以可以把同余式右侧的式子用哈希表存储(如有重复选择最大的 kkk 即可),然后从小到大枚举同余式左侧的 iii,如果对于某个 iii,等式左边的值可以在哈希表中查到,那么此时的 it−kit - kit−k 就是最小的 xxx。

代码不贴 ,因为懒。


如果 a⊥pa \perp pa⊥p 不成立怎么办?

根据模的分配律,我们设 g=gcd⁡(a,p)g = \gcd(a, p)g=gcd(a,p),那么原方程可以变成:
ax−1⋅ag≡bg ( mod  pg) a^{x - 1}\cdot\frac{a}{g} \equiv \frac{b}{g} ~(\bmod~\frac{p}{g}) ax−1⋅ga​≡gb​ (mod gp​)
所以当 b≠1b \ne 1b​=1 且 g∤bg \nmid bg∤b 时,该方程无解。

如果保证一直有解,那么我们将这个方程一直化开,直到 a⊥p∑i=1kgia \perp \frac{p}{\sum_{i = 1}^k g_i}a⊥∑i=1k​gi​p​。

此时有:(设 q=∑i=1kagiq = \sum_{i = 1}^k\frac{a}{g_i}q=∑i=1k​gi​a​)
ax−k≡bq∑i=1kgi ( mod  p∑i=1kgi) a^{x - k} \equiv \frac{b}{q \sum_{i = 1}^k g_i} ~(\bmod~\frac{p}{\sum_{i = 1}^k g_i}) ax−k≡q∑i=1k​gi​b​ (mod ∑i=1k​gi​p​)
这个时候就可以用 BSGSBSGSBSGS 求解了,最后的答案记得要加上 kkk。

当然,算法过程中还需要特判 q=b∑i=1kgiq = \frac{b}{\sum_{i = 1}^k g_i}q=∑i=1k​gi​b​ 的情况,此时左侧 aaa 的次数为 000,答案即为 kkk。

贴上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<map>

using namespace std;

int a, p, b;
map<int, int> Hash;

inline int Read()
{
	int v = 0; char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) v = (v << 3) + (v << 1) + c - '0', c = getchar();
	return v;
}

inline int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

inline void Exgcd(int a, int b, int &x, int &y)
{
	if(b == 0) return x = 1, y = 0, void();
	Exgcd(b, a % b, y, x);
	return y -= a / b * x, void();
}

inline int Inv(int a, int b)
{
	int x, y; Exgcd(a, b, x, y);
	return ((x % b) + b) % b;
}

inline int Pow(int a, int b, int p)
{
	int res = 1;
	while(b)
	{
		if(b & 1) res = 1LL * res * a % p;
		a = 1LL * a * a % p;
		b >>= 1;
	}
	return res;
}

inline int BSGS(int a, int b, int p)
{
	Hash.clear(), b %= p;
	int t = (int)(sqrt(p) + 1), w = 1;
	for(register int i = 0; i < t; i++)
	{
		int val = 1LL * b * w % p;
		Hash[val] = i;
		w = 1LL * w * a % p;
	}
	int val = 1; a = Pow(a, t, p), val = 1;
	if(!a) return b ? -1 : 1;
	for(register int i = 1; i <= t; i++)
	{
		val = 1LL * val * a % p;
		int j = Hash.find(val) == Hash.end() ? -1 : Hash[val]; 
		if((~j) && i * t - j >= 0) return i * t - j;
	}
	return -1;
}

inline int ExBSGS(int a, int b, int p)
{
	if(b == 1 || p == 1) return 0;
	int g = gcd(a, p), k = 0, q = 1;
	while(g ^ 1)
	{
		if(b % g) return -1;
		++k; b /= g, p /= g;
		q = 1LL * q * (a / g) % p;
		if(q == b) return k;
		g = gcd(a, p);
	}
	int res = BSGS(a, 1LL * b * Inv(q, p) % p, p);
	return (~res) ? res + k : res;
}

int main()
{
	#ifdef FILE
		freopen("Input.in", "r", stdin);
	#endif
	a = Read(), p = Read(), b = Read();
	while(a || p || b)
	{
		a %= p, b %= p;
		int ans = ExBSGS(a, b, p);
		if(ans == -1) puts("No Solution");
		else printf("%d\n", ans);
		a = Read(), p = Read(), b = Read();
	}
	return 0;
}


中国剩余定理

如果保证所有的 pip_ipi​ 之间两两互质,有如下算法:

设 M=∏i=1mpiM = \prod_{i = 1}^mp_iM=∏i=1m​pi​,Mi=MpiM_i = \frac{M}{p_i}Mi​=pi​M​,Mi−1M_i^{-1}Mi−1​ 为 MiM_iMi​ 在模 pip_ipi​ 意义下的逆元。

设每个方程的特解为 xix_ixi​,则方程组特解为 ∑i=1mxiMiMi−1\sum_{i = 1}^m x_iM_iM_i^{-1}∑i=1m​xi​Mi​Mi−1​。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>

#define MAXN 1005

using namespace std;

int n;
long long a[MAXN], b[MAXN], sum = 1, ans;

void exgcd(long long a, long long b, long long &x, long long &y)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return;
    }
    exgcd(b, a % b, x, y);
    int z = x;
    x = y;
    y = z - (a / b) * y;
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        scanf("%lld%lld", &a[i], &b[i]);
        sum *= a[i];
    }
    for(int i = 1; i <= n; i++)
    {
        long long m = sum / a[i], x = 0, y = 0;
        exgcd(m, a[i], x, y);
        ans += b[i] * m * (x < 0 ? x + a[i] : x);
    }
    printf("%lld", ans % sum);
    return 0;
}

如果不保证 pip_ipi​ 两两互质该怎么办?

假设现在我们已经得出了前 i−1i - 1i−1 个方程的通解 x+kM (k∈Z)x + kM~(k \in Z)x+kM (k∈Z),其中 MMM 显然为 LCMj=1i−1 pjLCM_{j = 1}^{i - 1}~p_jLCMj=1i−1​ pj​,那么对于第 iii 个方程,我们就是要找到一个 kkk 满足:

x+kM≡ai ( mod  pi) x + kM \equiv a_i ~(\bmod~p_i) x+kM≡ai​ (mod pi​)

显然这可以化为一个二元一次方程,用 exgcdexgcdexgcd 求解即可(同时可以用裴蜀定理判断无解情况)。

代码:(用上了龟速乘避免溢出)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cctype>

#define MAXN 100005

#define up(i, l, r) for(int i = (l); i <= (r); i = -~i)
#define down(i, l, r) for(int i = (l); i >= (r); i--)

using i64 = long long;
using namespace std;

namespace IO
{
	int f; char c;

	template<typename T> inline void _Read(T &v)
	{
		v = 0; f = 1; c = getchar();
		while(!isdigit(c)) { if(c == '-') f = -1; c = getchar(); }
		while(isdigit(c)) { v = (v << 3) + (v << 1) + (int)(c - '0'); c = getchar(); }
		v *= f;
		return;
	}

	template<typename T> inline void _Write(T k)
	{
		if(k < 0) { putchar('-'); k = -k; }
		if(k > 9) _Write(k / 10);
		putchar((char)(k % 10 + '0'));
		return;
	}

	inline int Read() { int v; _Read(v); return v; }
	inline i64 Read64() { i64 v; _Read(v); return v; }
	inline void Write(int v, char ed = '\n') { _Write(v); putchar(ed); return; }
	inline void Write64(i64 v, char ed = '\n') { _Write(v); putchar(ed); return; }
}

using IO :: Read;
using IO :: Read64;
using IO :: Write;
using IO :: Write64;

int n;
i64 b[MAXN], a[MAXN], mul, ans;

inline void Mul(i64 &res, i64 a, i64 b, i64 p)
{
	res = 0LL;
	while(b)
	{
		if(b & 1LL) res = (res + a) % p;
		a = (a << 1) % p;
		b >>= 1;
	}
	return;
}

inline i64 Exgcd(i64 a, i64 b, i64 &x, i64 &y)
{
	if(b == 0LL) return x = 1LL, y = 0LL, a;
	i64 c = Exgcd(b, a % b, y, x);
	return y -= (a / b) * x, c;
}

int main()
{
	n = Read();
	up(i, 1, n) a[i] = Read64(), b[i] = Read64();
	mul = a[1]; ans = b[1];
	up(i, 2, n)
	{
		i64 A = mul, B = a[i], C = ((b[i] - ans) % B + B) % B, x, y;
		i64 D = Exgcd(A, B, x, y), E = B / D;
		Mul(x, x, C / D, E);
		ans += mul * x; mul *= E;
		ans = (ans % mul + mul) % mul;
	}
	Write64(ans);
	return 0;
}


原根

  • 定义部分

设 a,pa,pa,p 互质且 p>1p > 1p>1,则使得同余式 an≡1 ( mod  p)a^n \equiv 1~(\bmod~p)an≡1 (mod p) 成立的最小的 nnn 被称为 aaa 模 ppp 的阶,记为 δp(a)\delta_p(a)δp​(a)。

由欧拉定理 aφ(p)≡1 ( mod p)a^{\varphi(p)} \equiv 1~(\bmod p)aφ(p)≡1 (modp),可知 δp(a) ∣ φ(p)\delta_p(a)~|~\varphi(p)δp​(a) ∣ φ(p)。

若存在 g⊥pg \perp pg⊥p 使得 δp(g)=φ(p)\delta_p(g) = \varphi(p)δp​(g)=φ(p),则称 ggg 为模 ppp 的原根。

  • 原根存在定理

一个大于 111 的整数 nnn 存在原根,当且仅当 n=2,4,pα,2pαn = 2,4,p^\alpha, 2p^\alphan=2,4,pα,2pα(其中 ppp 为奇素数,α\alphaα 为正整数)。

(由于笔者太菜,这里的证明省去。事实上,这里的证明的确十分复杂,并不建议深究。)

  • 原根个数

若 nnn 有原根,其个数为 φ(φ(n))\varphi(\varphi(n))φ(φ(n))。

如何证明?

设任一原根为 ggg,而阶存在性质 δn(gk)=δn(g)gcd⁡(δn(g),k)\delta_n(g^k) = \frac{\delta_n(g)}{\gcd(\delta_n(g), k)}δn​(gk)=gcd(δn​(g),k)δn​(g)​ (这里也略去证明,感兴趣的可以上网查找),说明如果 k⊥φ(n)k \perp \varphi(n)k⊥φ(n),那么 δn(gk)=δn(g)=φ(n)\delta_n(g^k) = \delta_n(g) = \varphi(n)δn​(gk)=δn​(g)=φ(n),即 gkg^kgk 也为 nnn 的原根。

可以发现这样的 kkk 恰好有 φ(φ(n))\varphi(\varphi(n))φ(φ(n)) 个,并且由于原根的定义,这些数互不相同。

而对于这样的一些 kkk,所有的 gkv mod n (v∈[1,φ(n)])g^{kv} \bmod n~(v \in [1, \varphi(n)])gkvmodn (v∈[1,φ(n)]) 也会互不相同,因为 gkg^kgk 模 nnn 的阶为 φ(n)\varphi(n)φ(n)。

所以,这些数确实为 nnn 的所有原根。

得证。

  • 原根求法

我们先枚举求出 nnn 的最小原根 ggg。

如何判定一个数 ggg 是否为 nnn 的原根呢?

如果 n=2n = 2n=2,原根只能为 111,可以直接判定。

否则,ggg 为模 nnn 的原根的充要条件是:对于 φ(n)\varphi(n)φ(n) 的每个质因数 ddd,都不满足 gφ(n)d≡1 ( mod  n)g^{\frac{\varphi(n)}{d}} \equiv 1 ~(\bmod~n)gdφ(n)​≡1 (mod n)。(再次略去证明)

这样,我们就可以先把 φ(n)\varphi(n)φ(n) 分解质因数,然后暴力枚举判断了。

为什么暴力枚举不会超时?

  • 我国数学家王元于 195919591959 年证明了若 nnn 有原根,其最小原根是不超过 n0.25n^{0.25}n0.25 级别的。所以枚举的时间复杂度在可承受范围内。

然后对于所有与 φ(n)\varphi(n)φ(n) 互质的 k∈[1,φ(n))k \in [1, \varphi(n))k∈[1,φ(n)), gk mod ng^k \bmod ngkmodn 均为 nnn 的原根。

贴上洛谷模板题代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<algorithm>

#define MAXN 1000001

using namespace std;

int t, n, d, phi[MAXN], len, pr[MAXN], cnt, ans[MAXN];
bool np[MAXN], rt[MAXN];

inline void Sieve()
{
    for(register int i = 2; i < MAXN; i++) phi[i] = i;
    for(register int i = 2; i < MAXN; i++)
    {
        if(!np[i])
        {
            rt[i] = true; if((i << 1) < MAXN) rt[i << 1] = true; phi[i] = i - 1;
            for(register int j = 2; i * j < MAXN; j++) np[i * j] = true, phi[i * j] = phi[i * j] / i * (i - 1);
            if(i & 1 && i <= 1000)
            {
                int tmp = i * i;
                while(tmp < MAXN) { rt[tmp] = true; if((tmp << 1) < MAXN) rt[tmp << 1] = true; tmp *= i; }
            }
        }
    }
    return;
}

inline void Divide(int k)
{
    len = 0;
    for(register int i = 2; i * i <= k; i++)
    {
        if(k % i == 0)
        {
            pr[++len] = i;
            while(k % i == 0) k /= i;
        }
    }
    if(k > 1) pr[++len] = k;
    return;
}

inline int pw(int a, int b, int p)
{
    int res = 1;
    while(b)
    {
        if(b & 1) res = 1LL * res * a % p;
        a = 1LL * a * a % p; 
        b >>= 1;
    }
    return res;
}

inline bool Check(int a, int b, int p)
{
    for(register int i = 1; i <= len; i++) if(pw(a, b / pr[i], p) == 1) return false;
    return true;
}

inline int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a % b); }

int main()
{
    #ifdef FILE
        freopen("Input.in", "r", stdin);
        freopen("Output.out", "w", stdout);
    #endif
    Sieve();
    scanf("%d", &t);
    while(t--)
    {
        cnt = 0;
        scanf("%d%d", &n, &d);
        if(!rt[n]) { puts("0\n"); continue; }
        Divide(phi[n]);
        int g; for(register int i = 1; i < n; i++) if(pw(i, phi[n], n) == 1 && Check(i, phi[n], n)) { g = i; break; }
        int val = 1;
        for(register int i = 1; i <= phi[n]; i++)
        {
            val = 1LL * val * g % n;
            if(gcd(i, phi[n]) == 1) ans[++cnt] = val;
        }
        printf("%d\n", cnt), sort(ans + 1, ans + cnt + 1);
        for(register int i = d; i <= cnt; i += d) printf("%d ", ans[i]);
        putchar('\n');
    }
    return 0;
}

二次剩余

下文仅介绍 ppp 为奇素数时的 CipollaCipollaCipolla 算法。

如无特殊情况,所有的定义、运算都是在  mod  p\bmod~pmod p 意义下的。


  • 定义部分

如果对于非 000 整数 nnn,使得 x2≡n ( mod  p)x ^ 2 \equiv n ~(\bmod~p)x2≡n (mod p) 无解,称 nnn 为二次非剩余,否则为二次剩余。

否则,我们来讨论一下解的情况。

设有一组不等的解为 x1,x2x_1,x_2x1​,x2​,则有 x12≡x22x_1^2 \equiv x_2^2x12​≡x22​,移项后有 (x1−x2)(x1+x2)≡0(x_1 - x_2)(x_1 + x_2) \equiv 0(x1​−x2​)(x1​+x2​)≡0,而前者不等于 000,所以 x1,x2x_1, x_2x1​,x2​ 必为相反数。

并且模意义下,由于 ppp 为奇素数,因此它们互相之间是不相等的。

也就是说,方程最多有两互异解,并且互为相反数。这样,我们就可以判断唯一解的情况了:直接判断 n=0n = 0n=0 是否成立,成立则 x=0x=0x=0 为唯一解。

进一步地,我们发现:任意一对相反数对应唯一一个二次剩余,所以二次剩余的个数为 p−12\frac{p - 1}{2}2p−1​,其它的非 000 整数即为二次非剩余,数量与其相等。

  • 欧拉判别准则

若 x⊥px \perp px⊥p ,当 xp−12≡1 ( mod  p)x ^ {\frac{p - 1}{2}} \equiv 1~(\bmod~p)x2p−1​≡1 (mod p) 成立时,xxx 为模 ppp 的二次剩余,否则为二次非剩余。

即 “xxx 是模 ppp 的二次剩余” 是 “xp−12≡1 ( mod  p)x ^ {\frac{p - 1}{2}} \equiv 1~(\bmod~p)x2p−1​≡1 (mod p) 成立” 的充要条件。

充分性:设 x≡k2 ( mod  p)x \equiv k ^ 2 ~(\bmod~p)x≡k2 (mod p),由费马小定理可知 xp−12≡kp−1≡1 ( mod  p)x ^ {\frac{p - 1}{2}} \equiv k ^ {p - 1} \equiv 1 ~(\bmod~p)x2p−1​≡kp−1≡1 (mod p)。

必要性:设 ggg 为 ppp 的一个原根,由于 x⊥px \perp px⊥p,则必定存在一个正整数 kkk 使得 x≡gk ( mod  p)x \equiv g^k ~(\bmod~p)x≡gk (mod p) 。又因为 xp−12≡gk(p−1)2≡1 ( mod  p)x^{\frac{p - 1}{2}} \equiv g^{\frac{k(p - 1)}{2}} \equiv 1 ~(\bmod~p)x2p−1​≡g2k(p−1)​≡1 (mod p),所以 k(p−1)2{\frac{k(p - 1)}{2}}2k(p−1)​ 一定是 φ(p)=p−1\varphi(p) = p - 1φ(p)=p−1 的倍数,说明 kkk 是一个偶数。所以令 m=gk2m = g^{\frac{k}{2}}m=g2k​ 就有 x≡m2 ( mod  p)x \equiv m^2 ~(\bmod~p)x≡m2 (mod p)。

综上,我们只要判断 xp−12≡1 ( mod  p)x ^ {\frac{p - 1}{2}} \equiv 1~(\bmod~p)x2p−1​≡1 (mod p) 是否成立,就可以求出 xxx 是否为二次剩余。

  • 求法

判定是否为二次剩余后,我们怎样求出方程的解呢?

所以,下面介绍 CipollaCipollaCipolla 算法。

对于二次剩余方程 x2≡n ( mod  p)x^2 \equiv n ~(\bmod~p)x2≡n (mod p),我们先随机出一个 kkk 使得 k2−nk ^ 2 - nk2−n 为二次非剩余,由于其个数与二次剩余相等,因此期望 222 次就能找到。

然后我们类似虚数,定义 i2≡k2−n ( mod  p)i ^ 2 \equiv k ^ 2 - n ~(\bmod~p)i2≡k2−n (mod p)。

接下来摆定理:x=(k+i)p+12x = (k + i) ^ {\frac{p + 1}{2}}x=(k+i)2p+1​ 为原方程的一个解。

证明:

首先通过二项式定理展开,再用 LucasLucasLucas 定理化简得 (k+i)p≡kp+ip ( mod  p)(k + i) ^ p \equiv k^p + i ^ p ~(\bmod~p)(k+i)p≡kp+ip (mod p)。

根据欧拉判别法有:
(i2)p−12≡ip−1≡−1 ( mod  p)ip≡−i ( mod  p) \begin{aligned} &(i ^ 2) ^ {\frac{p - 1}{2}} \equiv i ^ {p - 1} \equiv -1~(\bmod~p) \\ &i ^ p \equiv -i ~(\bmod~p) \end{aligned} ​(i2)2p−1​≡ip−1≡−1 (mod p)ip≡−i (mod p)​
由费马小定理可知 kp≡k ( mod  p)k ^ p \equiv k ~(\bmod~p)kp≡k (mod p)。

所以:
x2≡(k+i)p+1≡(k+i)p(k+i)≡(kp+ip)(k+i)≡(k−i)(k+i)=k2−i2=n \begin{aligned} x ^ 2 &\equiv (k + i)^{p + 1} \\ & \equiv (k + i) ^ p (k + i) \\ & \equiv (k ^ p + i ^ p)(k + i) \\ & \equiv (k - i)(k + i) \\ & = k ^ 2 - i ^ 2 = n \end{aligned} x2​≡(k+i)p+1≡(k+i)p(k+i)≡(kp+ip)(k+i)≡(k−i)(k+i)=k2−i2=n​
得证。

贴上代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<ctime>

using LL = long long;
using namespace std;

int t;
LL si, n, p;

struct Complex
{
	LL a, b;
	
	Complex operator * (const Complex &t) const
	{
	 	Complex res = (Complex){0LL, 0LL};
		res.a = (a * t.a % p + b * t.b % p * si % p) % p;
		res.b = (a * t.b % p + b * t.a % p) % p;
		return res;
	}
};

inline LL Pow_Real(LL a, LL b)
{
	LL res = 1LL;
	while(b)
	{
		if(b & 1LL) res = res * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return res;
}

inline LL Pow_Complex(Complex a, LL b)
{
	Complex res = (Complex){1LL, 0LL};
	while(b)
	{
		if(b & 1LL) res = res * a;
		a = a * a;
		b >>= 1;
	}
	return res.a;
}

inline LL Solve(LL n, LL p)
{
	if(Pow_Real(n, (p - 1LL) / 2LL) != 1LL) return -1LL;
	LL k;
	while(true)
	{
		k = 1LL * rand() * rand() % p;
		si = ((k * k % p - n) % p + p) % p;
		if(Pow_Real(si, (p - 1LL) / 2LL) != 1LL) break;
	}
	Complex val = (Complex){k, 1};
	return Pow_Complex(val, (p + 1LL) / 2LL);
}

int main()
{
	#ifdef FILE
		freopen("Input.in", "r", stdin);
	#endif
	scanf("%d", &t);
	while(t--)
	{
		scanf("%lld%lld", &n, &p), n %= p;
		if(n == 0LL) { puts("0"); continue; }
		LL ans1 = Solve(n, p), ans2;
		if(ans1 == -1LL) { puts("Hola!"); continue; }
		else
		{
			ans2 = p - ans1;
			if(ans1 > ans2) swap(ans1, ans2);
			printf("%lld %lld\n", ans1, ans2);
			continue;
		}
	}
	return 0;
}

To Be Continued⋯⋯\mathit{To~Be~Continued\cdots\cdots}To Be Continued⋯⋯

深入解析能被11整除的数的特征:从同余原理到编程实现 在数论与计算机科学中,数的整除性判断是一个基础而重要的概念,它基于同余理论,为数字处理提供了高效的理论工具。其核心原理在于分析数字在特定进制下的位值表示与模运算的关系,从而推导出简洁的判别法则。这种技术不仅具有理论价值,更能直接应用于算法优化、数据校验和心算加速等工程实践。例如,在ISBN-10校验码计算和大数质因数分解等场景中,高效的整除性判断算法至关重要。本文以“能被11整除的数的特征”为具体案例,深入剖析其背后的同余原理,并详细拆解了从“奇偶位差法”到“凑11消去法”等多种实战技巧,最后给出了健壮的P 阅读详情

相关推荐

信息学奥赛之数学一本通 C++版 用编程的方式学数学

适用对象包括:中学信息学奥林匹克竞赛选手及辅导老师、大学AcM程序设计比赛选手及教练、高等院校计算机相关专业的师生、程序设计爱好者等。数学是计算机程序设计的灵魂。利用数学方面的知识、数学分析的方法以及数学题解的技巧,可以使得程序设计变得轻松、美观、高效,而且往往能反映出问题的本质。在国内外各项程序设计比赛(比如,ACM、NOI)活动中,越来越多地用到各种复杂的数学知识,对选手的数学修养要求越来越高。

星卯教育-信奥教练tony 2367

【数论-Lucas定理】

1.写在前面:我始终觉得,对于一个问题要知其然,更要知其所以然。Lucas定理在刚刚接触数论的时候就知道了,因为这是一个很常用的定理,常常和中国剩余定理放在一起考。最近在组合数学上出现了很多问题,但是都是找个结论就过去了。浑浑噩噩并不懂其中原理,感觉自己的数学直觉一直在下降,以前我甚至能够从数据中看出来数学表达式,后来学了很多的工具,就变懒了,甚至连一个稍微复杂一点的积分都懒得动手,直接自适应Si...

YG爱木木 1万+

27、同余问题研究与相关数学概念探讨

本文系统研究了同余问题在数论和组合数学中的应用与相关数学概念,涵盖了二项式系数、斯特林数、贝尔数等的奇偶性判断、整除性证明、模运算性质以及函数关系。文章探讨了同余理论与细胞自动机、多项式系数同余、p-进数理论等的联系,并总结了相关研究成果与未来研究方向,为深入理解数学结构和推动同余理论跨学科发展提供了重要参考。

opencv7vision的博客 189

威尔逊定理

在初等数论中,威尔逊定理给出了判定一个自然数是否为素数的充分必要条件。 即:当且仅当p为素数时:( p -1 )! ≡ -1 ( mod p ),但是由于阶乘是呈爆炸增长的,其结论对于实际操作意义不大。 即:若p为质数,则p可整除(p-1)!+1。 ...

Valieli的博客 1160

27、同余问题的深入探讨

本文深入探讨了同余问题在组合数学与数论中的广泛应用,涵盖二项式系数、斯特林数、Bell数、斐波那契数、错排数和调和数等特殊函数的奇偶性、整除性及模素数性质。通过练习题与理论分析相结合,展示了Lucas定理、Touchard同余、Spivey公式等重要结果,并介绍了p-进赋值、细胞自动机与谢尔宾斯基三角形等关联概念。文章还综述了相关领域的研究进展与未来方向,包括组合数的素性判定、同余式的推广以及同余理论在物理学中的潜在应用。

ff678的博客 52

【初等数论】整除、公约数、同余与剩余系

整除、公约数、同余与剩余系 从本文开始,我们将正式开始介绍有关初等数论的相关知识与概念,我们争取用通俗的语言去把握和描述理论的精髓所在。而不拘泥于具体概念的束缚,以窥探初等数论巧妙的一些思想方法。从这里开始你的行囊里不需要太多的东西,只要会整数的加减乘除即可。东西多了不仅帮不了你,反而会成为前进的负担。你需要首先抛开一切固有思维,清空大脑,带着孩童般的好奇心重新认识这个世界。由于数论经常出现于奥数和智力题中,它往往被当成一种锻炼思维的智力游戏,但随着研究的深入,我们需要建立一套理论才能看清本质。我们可以从最

smilejiasmile的博客 1197

数论 —— 整除与同余

【整除】 1.定义 若qZ, s.t. b=a*q ,则称 b 被 a 整除,记作 a|b,其中,b 是 a 的倍数,a 是b 的约数。 2.性质 1)若 a|b 且 b|c,则:a|c 2)若 a|b 且 a|c,对于x,yZ,有:a|(b*x+c*y) 3)若 a|b 且m0,则:(m*a)|(m*b) 4)若a|n 且 b|n,当x,yZ,满足...

Alex_McAvoy的博客 5049

在Docker中集成GPU监控体系

先上效果图 等文档整理出来再更

tamako0v0的博客 2470

基础数论算法(1)整除与同余

整除 同余

LiRewriter的博客 2525

2024高教社杯数学建模国赛B题丨国一原创论文解析及源码超详细分享!

继续分享一篇2024国赛B题的国奖论文,包含可运行源代码jupyter,国赛B题通常比较聚焦实际复杂系统的多目标优化决策,主要偏重算法实现与概率建模解决开放性的管理问题。而且高度开放,无标准答案,学科交叉复杂,如资源调度、交通优化、海洋探测等。比较建议有一定统计学基础的同学参加。

ihui永不放弃 6738

告别“单打独斗”:Oh My Open Code 如何用多模型协作重塑 AI 编程范式

介绍"Oh My OpenCode"——一个能协调多个专业AI模型完成复杂开发任务的智能编程框架。您将看到它如何分配任务、发挥不同模型的独特优势,并通过并行处理和实时纠错等功能解决AI编程流程中的常见痛点。

老孙 | Ethan Sun 的博客:AI 智能体与 AI 原生软件的工程实践 4208

OI基础数论

纯纯是研究理论数学的烧脑分支呀!!让我们看看bing给出的定义:初等数论主要就是研究整数环的整除理论及同余理论。此外它也包括了连分数理论和少许不定方程的问题。本质上说,初等数论的研究手段局限在整除性质上。初等数论中经典的结论包括算数基本定理、欧几里得的质数无限证明、中国剩余定理、欧拉定理(其特例是费马小定理)、二次互反律, 勾股方程的商高定理、佩尔方程的连分数求解法等等。

MaydayMerlin的博客 695

STM32单片机远程升级

stm32远程升级

fang_yang_wa的博客 1万+

【数学】数论干货(疑似密码学基础)

若整数 bbb 除以非零整数 aaa,商为整数,且余数为零,bbb 为被除数,aaa 为除数,即 a∣ba\mid ba∣b(“∣\mid∣”是整除符号),读作“a整除b”或“b能被a整除”。例如:4整除16写作”4 ∣\mid∣ 16“。(注意:“∣\mid∣” 之前的数为除数,之后的为被除数,别搞反了。)内容:设整数 N>1N>1N>1,那么必有 N=p1p2⋯pnN=p_1p_2\cdots p_nN=p1​p2​⋯pn​,其中 p1,p2,⋯,pnp_1,p_2,\cdots,p_np1​,p2​,

2401_87820834的博客 1571

用友YonSuite产品实操手把手教学(图文版)

用友YonSuite

ct341688的博客 8158

【RT-DETR涨点改进】全网独家创新、细节涨点改进篇 | ICML 2024 | RT-DETR引入ICB交互式卷积模块和ASB自适应频谱模块,助力目标检测、异常检测有效涨点、含多种创新改进点

RT-DETR目标检测模型通过引入ICB和ASB模块实现性能提升。ICB模块采用多尺度卷积交互,有效捕获局部和长程依赖特征;ASB模块通过自适应频域滤波优化特征表示,降低噪声干扰。两者结合使模型在复杂场景中表现更优,显著提升检测精度和鲁棒性。文章详细介绍了模块原理、代码实现及配置方法,并提供了8种改进版本的YAML配置文件,适用于不同网络结构。这些创新改进为RT-DETR模型提供了高效涨点方案,特别适合复杂背景和高噪声环境下的目标检测任务。

Ai缝合怪、助力小伙伴高效发论文 211

7.25 7.26集训——初等数论

整除理论 素数定理 算数基本定理 素数判定 单点 O(​编辑) 埃氏筛 O(nloglogn) 线性筛 O(n) 整除理论 辗转相除法 O(log(a+b)) 同余理论 exgcd O(log(a+b)) 乘法逆元 费马小定理 威尔逊定理 线性推逆元 O(n) a在模m下的乘法逆元 例题 中国剩余定理 扩展中国剩余定理 欧拉函数 莫比乌斯函数 欧拉定理 扩展欧拉定理 Lucas定理 ...

DL24zhanghao的博客 379

Central Finance 全景解析:用 SAP S/4HANA 构建跨系统的财务中台与渐进式转型路径

Central Finance 是基于 SAP S/4HANA 的一种部署与应用场景:在不打断现有异构源系统运营的前提下,把多套 SAP 与非 SAP 系统中的财务与管理会计数据实时/准实时复制到一套中心化的 SAP S/4HANA 中,作为统一的记账与分析底座,逐步把选定的财务流程(如预算控制、付款、固定资产结账等)迁入中心系统。这让企业既能快速获得统一报表与统一核算结构,又为未来的整体迁移留出了弹性路径。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 388

C++基础数论

本文介绍了数论中求逆元的五种方法:费马小定理(适用于模数为质数)、拓展欧几里得算法(通用解法)、顺推法(求连续数的逆元)、逆推法(通过阶乘求逆元)以及前后缀乘积法(结合快速幂)。此外还讲解了整除性质、欧拉函数及其反演、同余定理(欧拉定理和费马小定理)以及中国剩余定理的原理与应用。文章通过推导公式和代码示例,系统总结了数论基础知识,适合初学者系统学习数论内容。

adminh_miao的博客 96

C语言试题三之计算并输出 s=1+(1+2^(0.5))+(1+2^(0.5)+3^(0.5))+…+(1+2^(0.5)+3^(0.5)+…+n^(0.5))

1 、题目 计算并输出 s=1+(1+2^(0.5))+(1+2^(0.5)+3^(0.5))+…+(1+2^(0.5)+3^(0.5)+…+n^(0.5)) 2、部分函数说明 C 库函数 double pow(double x, double y) 返回 x 的 y 次幂。 3、源代码实现 #include <stdio.h> #include <...

码莎拉蒂 4609

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

数论大杂烩——From GDKOI2021 【数论基础】【学习笔记】

数论大杂烩——From GDKOI2021 【数论基础】【学习笔记】

JA_yichao欢迎你 cnblogs账号:https://www.cnblogs.com/mayichao/ 323

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

数论基础@整除相关概念和性质@带余除法定理

文章目录abstract数论的主要内容数论的重要性和应用整除整除的定义整除@约数@倍数注意正约数相关概念的辨析整除(Divisibility)被整除(Being Divisible)除以@除整除的性质👺举例应用👺其他性质正约数性质1方法1方法2补充例正约数性质2有意思的整除问题或性质带余除法定理存在性唯一性带余除法和正负号余数和取模相关性质自然数可以表示为任意进制数利用带余除法做整数的进制转换小数点移位将qqq进制数化为10进制数将10进制数化为qqq进制数例 abstract 数论(Number Theo

xuchaoxin1375的博客 2423

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

数论——同余

理论背景 数学上,两个整数除以同一个整数,若得相同余数,则二整数同余(英文:Modular arithmetic,德文:Kongruenz)。同余理论常被用于数论中。最先引用同余的概念与符号者为德国数学家高斯。同余理论是初等数论的重要组成部分,是研究整数问题的重要工具之一,利用同余来论证某些整除性的问题是很简单的。同余是数学竞赛的重要组成部分。 同余定理 给定一个正整数 m,如果两个整数 a...

努力中的老周的专栏 5380

同余

数论中的重要概念。给定一个正整数m,如果两个整数a和b满足a-b能被m整除,即m|(a-b),那么就称整数a与b对模m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系。 目录 1理论背景 2同余符号 3性质 4相关定理 1理论背景编辑 数学上 同余 ,

javanpl123456的专栏 988

数论:从同余的观点出发

《数论:从同余的观点出发》基本信息作者: 蔡天新 [作译者介绍]出版社:高等教育出版社ISBN:9787040348347上架时间:2012-9-13出版日期:2012 年9月开本:16开页码:1版次:1-1所属分类: 数学更多关于 》》》《数论:从同余的观点出发》内容简介书籍数学书籍  《数论:从同余的观点出发》依据作者多年数论教学心得和研究成果写成。从同余的定义和观点出发,前五章依次讲述整除的...

weixin_33939843的博客 222
上一篇: 函数与表达式的反演变换
下一篇: 博弈论之SG函数
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值