数论再谈

最大公约数 和 最小公倍数

\(a\)\(b\) 最大公约数为 \(\gcd(a, b)\), 记 \(a\)\(b\) 最小公倍数数为 \(lcm(a, b)\)
\(a > b\)

求最大公约数

\(gcd(a, b) = gcd(b, a \pmod b)\)

证明

如果 \(a\)\(b\) 的倍数, 那么 \(\gcd(a,b) = b\),如果 \(b = 0\), 那么 \(\gcd(a, b) = a\)

下面讨论 \(a\) 不是 \(b\) 的倍数的情况。

\(a = bk + r\),那么 \(a \pmod b = r\),就是要证明 \(\gcd(a, b) = \gcd(b, r)\)

\(\gcd(a, b) = x\), 于是就有 \(a | x\)\(b | x\)

把上面的式子变一下,就是 \(r = a - bk\),同时除以一个 \(x\),于是 \(\frac{r}{x} = \frac{a}{x} - \frac{bk}{x}\)

\(\because a | x\), \(b | x\)

\(\therefore a | x, bk | x\)

\(\therefore \frac{a}{x},\frac{bk}{x} \in \mathbb{Z}\)

\(\therefore \frac{a}{x} - \frac{bk}{x} \in \mathbb{Z}\)
\(\frac{r}{x} \in \mathbb{Z}\)

\(\therefore r | x\)

那么 \(a, b\) 的约数就是 \(b, r\) 的约数,就可以得到 \(\gcd(a, b) = \gcd(b, r) = \gcd(b, a \pmod b)\)

得证。

求最小公倍数

\(lcm(a, b) = a \div gcd(a, b) \times b\)

证明

由唯一分解可以得到
\(a = p_1^{a_1} \times p_2^{a_2} \times \dots \times p_n^{a_n}\)
\(b = p_1^{b_1} \times p_2^{b_2} \times \dots \times p_n^{b_n}\)

那么最大公约数就是
\(\gcd(a, b) = p_1^{\min(a_1, b_1)} \times p_2^{\min(a_2, b_2)} \times \dots \times p_n^{\min(a_n, b_n)}\)

最小公倍数就是
\(lcm(a, b) = p_1^{\max(a_1, b_1)} \times p_2^{\max(a_2, b_2)} \times \dots \times p_n^{\max(a_n, b_n)}\)

显然 \(\gcd(a, b) \times lcm(a, b) = a \times b\)

因此就有 \(lcm(a, b) = a \times b \div \gcd(a, b)\)

然后还可以整一下,设 \(x = \gcd(a, b)\)

于是 \(a = x \times a',b = x \times b'\),于是就有 \(gcd(a', b') = 1\)

那么 \(lcm(a, b) = a' \times x \times b' \times x \div x = a' \times x \times b'\),可以发现这显然是最优的。

扩展欧几里得算法

这个算法可以求解形如这样的二元一次方程 \(ax + by = gcd(a, b)\), 其中 \(a,b\in \mathbb{Z}\)

求特解

不难发现有无数组解,我们这样设:

\(ax_1 + by_1 = gcd(a, b) \\ bx_2 + (a\pmod b)y_2 = gcd(a, b \pmod a) \)

由前面的欧几里得算法可以得到 \(gcd(a, b) = gcd(a, b \pmod a)\), 于是就有 \(ax_1 + by_1 = bx_2 + (a\mod b)y_2\)

然后又有 \(a\pmod b = a - (\lfloor \frac{a}{b} \rfloor \times b)\)

于是就有 \(ax_1 + by_1 = bx_2 + (a - (\lfloor \frac{a}{b} \rfloor \times b))y_2\)

于是 \(ax_1 + by_1 = ay_2 + b(x_2 - (\lfloor \frac{a}{b} \rfloor) \times y_2)\)

因为 \(a = a, b = b\), 所以 \(x_1 = y_2, y_1 = x_2 - (\lfloor \frac{a}{b} \rfloor) \times y_2\)

可以发现上面设的分别是欧几里得算法前后两种状态,所以在求欧几里得的时候就可以顺便把这组解求了。

求最小解

先给出一个结论:

\(x = x_1 + \frac{b}{\gcd(a, b)}\)
\(y = y_1 - \frac{a}{\gcd(a, b)}\)

可以显然的发现带入式子依然成立,因为加了一个 \(\frac{ab}{\gcd(a, b)}\) 又减了一个 \(\frac{ab}{\gcd(a, b)}\)

于是就可以得到 \(x\) 的值是有周期性的,然后求最小值就是 \(x1 \mod \frac{b}{\gcd(a, b)}\)

当 c 是任意的数时

因为扩展欧几里得算法求的是 \(ax + by = \gcd(a, b)\),现在我们要求 \(ax + by = c\) 的不定方程如何求呢?

显然我们让整个式子乘一个 \(c\) 再除一个 \(\gcd(a,b)\) 就可以了,如果 要求解为整数,那么就要 \(c | \gcd(a, b)\) 来保证整个变换中没有小数的出现。

我们求了一组 \(ax + by = \gcd(a, b)\) 的特解后,左右同时子乘一个 \(c\) 再除一个 \(\gcd(a,b)\) 式子依然成立,于是就有

\(a \times \frac{c}{\gcd(a, b)} \times x + b \times \frac{c}{\gcd(a, b)} \times y = c\)

于是解就是 \(\frac{c}{\gcd(a, b)} \times x\)

求解模方程

模方程也叫同余方程,是形如这样的方程 \(ax \equiv b\pmod c\)

求解同余方程

如何求解?我们把这个式子变换一下得到:
\(ax - b = cy\)

可以显然推到,然后移相得到 \(ax - cy = b\),就变成了一个不定方程,所以我们就可以用扩欧解决。

这里 \(c\) 可以取一个负号就是形如这样的式子了 \(ax + cy = b\)

逆元

随便说说,如果存在一个数 \(x\) 满足 \(ax\equiv 1 \pmod P\),我们就称 \(x\) 使 \(a\) 关于 \(P\) 的乘法逆元,记作 \(a^{-1}\),其实就是为了让模运算中存在除法而创造的一个东西。

然后求法这里提供 2 种

exgcd 求逆元

显然就是求 \(ax + Py = 1\),满足有解的必要条件是 \(\gcd{a, P} = 1\),我们就有了一种可以判断是否有解的方法。

直接求就完了

int inv(int a, int b) {
	int x, y;
	exgcd(a, b, x, y);
	return (x + b) % b;
}

费马小定理求逆元

费马小定理是如果 \(P\) 是质数就有

\[a^{P-1} \equiv 1 \pmod P \]

于是就有

\[a^{P-2}\times a \equiv 1 \pmod P \]

所以直接用快速幂求逆元就完了。

int inv(int a, int b) {
	return power(a, b - 2);
}

Problems

「NOIP2012」同余方程

题意很清楚了,我们按照上面的东西化简一下就可以得到 \(ax + by = 1\)

然后题目说一定有解,所以直接上 exgcd 模板就可以了。

i64 a, b;
std::cin >> a >> b;

i64 x, y;
exgcd(a, b, x, y);

std::cout << (x + b) % b << std::endl;

「POJ1061」青蛙的约会

题意很清楚了,求这个东西的最小解 \(x + nk \equiv y + mk \pmod L\)

依旧是推式子,可以得到 \((n - m) k + ly = y - x\),然后大力带模板就完了.

// k(m - n) - lz = -x + y;
// k(n - m) + lz = x - y;

i64 G = gcd(n - m, l);

if (std::abs(x - y) % G) {
	std::cout << "Impossible\n";
	return 0;
}

i64 mod = std::abs(l / G);
i64 x1, yy;
exgcd(n - m, l, x1, yy);
x1 = (1ll * x1 * (x - y) / G % mod + mod) % mod;
// x1 = x1 * (x - y) / G;
std::cout << x1 << std::endl;

「POJ2115」C Looooops

求这样的模方程 \(a + cx\equiv b(\pmod2^k)\)

于是就有 \(cx + 2^ky = b - a\)
带 exgcd 进去就完了

i64 a, b, c, k;
while (std::cin >> a >> b >> c >> k) {
	if (!a && !b && !c && !k) return 0;
	i64 A = c, B = (1ll << k), C = b - a;
	if (A == 0) {
		if (std::abs(C) % B) {
			std::cout << "FOREVER\n";
			continue;
		} else {
			std::cout << "0\n";
			continue;
		}
	}
	i64 x1, yy;
	exgcd(A, B, x1, yy);
	if (C % gcd(A, B)) {
		std::cout << "FOREVER" << "\n";
		continue;
	}
	i64 mod = std::abs(B / gcd(A, B));
	i64 ans = (x1 * C / gcd(A, B) % mod + mod) % mod;
	std::cout << ans << '\n';
}

「NOI2002」荒岛野人

找到最小的 \(m\) 使这个式子对于任意的 \(i\)\(j\) 成立

\[c_i + p_ix \equiv c_j + p_jx \pmod(m) \]

于是就有

\[(p_i-p_j)x+my=y-x \]

然后暴力求 \(m\),用 exgcd 判断一下就可以了。
其中最小解要满足在这些人寿命之外才会相遇。

int mx = -1;
std::vector<int> c(n), p(n), l(n);
for (int i = 0; i < n; i++) {
	std::cin >> c[i] >> p[i] >> l[i];
	mx = std::max(mx, c[i]);
}

auto check = [&](int m) {
	for (int i = 0; i < n; i++) {
		for (int j = i + 1; j < n; j++) {
			int A = p[i] - p[j], B = m, C = c[j] - c[i];
			int D = gcd(A, B);
			if (C % D) {
				continue;
			}
			int x, y;
			exgcd(A, B, x, y);
			int md = std::abs(B / D);
			x = (x * C / D % md + md) % md;
			if (x <= l[i] && x <= l[j]) {
				return false;
			}
		}
	}
	return true;
};

for (; ; mx++) {
	if (check(mx)) {
		std::cout << mx << std::endl;
		return 0;
	}
}

BSGS 和 exBSGS

BSGS

BSGS 算法可以在 \(O(\sqrt{p})\) 的时间复杂度内求得满足 \(a^x \equiv b\pmod P\)\(x\) 的值。其中满足 \(a\perp P\)\(\perp\) 表示互质,你知道就当我没说。

我们设 \(x = i\times \lceil \sqrt{P} \rceil - j\),于是就可以得到 \(a^{i\times \lceil \sqrt{P} \rceil} \equiv b\times a^j \pmod P\)

我们暴力求 \(b\times a^j\) 的所有情况,用 map 存,然后再枚举左边那个式子就可以了。

i64 m = std::ceil(std::sqrt(P));
a %= P, b %= P;
std::map<i64, i64> mp, vis;
for (int i = 0; i <= m; i++) {
	i64 w = b * power(a, i) % P;
	mp[w] = i;
	vis[w] = 1;
}

for (int i = 0; i <= m; i++) {
	i64 w = power(a, i * m) % P;
	if (vis[w]) {
		i64 x = mp[w];
		if ((i * m - x) % P >= 0 && x >= 0) {
			std::cout << (i * m - x) % P << std::endl;
			return 0;
		}
	}
}

std::cout << "no solution" << std::endl;

为什么要求 \(a\perp P\)

如果没有这个条件,那我们就无法左右两边同时乘一个 \(a^j\)

[SDOI2013] 随机数生成器

做这个题顺便就把等比数列学了。。。

等比数列就是满足这个条件的数列, \(a_i = a_1\times p^{i - 1}\)

然后有一个求和公式啊,这里我们简单的推导一下:

\[\sum_{i=1}^{n}a_i=a_1+a_1\times p + a_1\times p^{2} + \dots + a_1\times p^{n-1}\\ \]

同时乘上一个 \(p\),就有:

\[p\times \sum_{i=1}^{n}a_i=a_1\times p+a_1\times p^{2} + a_1\times p^{3} + \dots + a_1\times p^{n} \]

上下两个式子相减得到:

\[(1-p) \times \sum_{i=1}^{n}a_i = a_1 - a_1\times p^{n} \]

额,这就出来了:

\[\sum_{i=1}^{n}a_i=\frac{a_1\times (1-p^n)}{1-p} \]

然后再反观这个题。

随便写前面几项,找找有没有规律:

\[\left\{ \begin{array}{lcl} x_2=x_1\times a+b\\ x_3=x_1\times a^2+a\times b + b\\ x_4=x_1\times a^3+a^2\times b+a\times b + b\\ \dots \end{array} \right. \]

显然有规律,直接表示出来就是 \(x_i=x_1\times a^{i-1}+b\times \sum_{j=0}^{i-2}a^j\),于是就可以用我们的等比数列了。

显然 \(x_i=x_1\times a^{i-1}+b\times \frac{1-a^{i-1}}{1-a}\),我们不是要求 \(t\) 吗,带进去就完了。

\(t\equiv x_1\times a^{i-1}+b\times \frac{1-a^{i-1}}{1-a}\pmod{p}\)
下面就是愉快的推式子时间:

\[t\equiv x_1\times + \frac{b}{1-a} - \frac{a^{i-1}\times b}{1-a} \pmod{p}\\ t\equiv a^{i-1}\times (x_1-\frac{b}{1-a})+\frac{b}{1-a} \pmod{p}\\ t-\frac{b}{1-a}\equiv a^{i-1}\times (x_1-\frac{b}{1-a}) \pmod{p}\\ a^{i-1}\equiv \frac{t-\frac{b}{1-a}}{x_1-\frac{b}{1-a}} \pmod{p}\\ \]

用 BSGS 求就完了,然后答案再加一个 1。

exBSGS

如果没有互质的条件,我们如何求?

我们可以尝试创造这个条件,然后再用 BSGS 求解。

可以这样,设 \(d=\gcd(a,P)\),现在我们变换一下原式子:

\(\frac{a}{d}\times a^{x-1} \equiv \frac{b}{d}\pmod{\frac{p}{d}}\)

现在令 \(a'=a^{x-1},b'=\frac{b}{d},p'=\frac{p}{d}\)。就这样一直算,直到 \(d=1\),就是 \(a, p\) 互质了,然后再套上 BSGS 就完了,注意这里要记得求 \(\frac{a}{d}\) 的逆元哦。

最后化简成这个形式

\(\frac{a^{cnt}}{d} \times a^{x-cnt} \equiv b' \pmod{p'}\)

只要满足了BSGS的条件套上就能用,再搞一下就是 \(a^{x-cnt} \equiv b' \times \frac{a^{cnt}}{d} \pmod{p'}\)

如果 \(\frac{a^{cnt}}{d} = b'\),那么显然最小的答案就是现在的 \(cnt\)


using i64 = long long;
void exgcd(i64 a, i64 b, i64 &x, i64 &y) {
	if (!b) {
		x = 1, y = 0;
		return ;
	}
	exgcd(b, a % b, x, y);
	i64 t = x;
	x = y;
	y = t - a / b * y;
}

i64 inv(i64 a, i64 P) {
	i64 x, y;
	exgcd(a, P, x, y);
	return (x % P + P) % P;
}

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

i64 P, a, b;
i64 power(i64 a, i64 b) {
	i64 ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % P;
		a = a * a % P;
		b >>= 1;
	}
	return ans;
}
i64 BSGS(i64 a, i64 b, i64 P) {
	i64 m = std::ceil(std::sqrt(P));
	a %= P, b %= P;
	std::unordered_map<i64, i64> mp;
	for (int i = 0; i <= m; i++) {
		i64 w = b * power(a, i) % P;
		mp[w] = i;
	}
	for (int i = 0; i <= m; i++) {
		i64 w = power(a, i * m) % P;
		if (mp.find(w) != mp.end()) {
			i64 x = mp[w];
			if (i * m - x >= 0) {
				return (i * m - x + P) % P;
			}
		}
	}
	return -1ll;
}

i64 exBSGS(int a, int b, int P) {
	if (b == 1 || P == 1) {
		return 0;
	}
	int d, t = 1, cnt = 0;
	while ((d = gcd(a, P)) ^ 1) {
		if (b % d) {
			return -1;
		}
		P /= d, b /= d; cnt++;
		t = (1ll * t * (a / d)) % P;
		if (t == b) {
			return cnt;
		}
	}
	i64 ans = BSGS(a, b * inv(t, P) % P, P);
	if (ans == -1) return -1ll;
	return ans + cnt;
}

中国剩余定理和扩展中国剩余定理

中国剩余定理

求满足条件的最小的 \(x\)。其中 \(b\) 两两互质。

\[\left\{ \begin{array}{lcl} x \equiv a_1 \pmod {b_1}\\ x \equiv a_2 \pmod {b_2}\\ x \equiv a_3 \pmod {b_3}\\ \dots\\ x \equiv a_{n-1} \pmod {b_{n-1}}\\ x \equiv a_n \pmod {b_n}\\ \end{array} \right. \]

先给出一个结论,令 \(B = \prod^n_{i} b_i\),答案就是 \(\sum^{n}_i a_i \times \frac{B}{b_i} \times inv(\frac{B}{b_i})\)

\(inv\) 表示逆元。

证明

老祖宗的智慧。

显然 \(\forall i, j\in{[1,n]},i\neq j\) 都满足 \(\gcd(b_i, b_j)=1\)于是有 \(\gcd(b_i, \frac{B}{b_i})=1\)

于是就存在 \(\frac{B}{b_i}\) 关于 \(b_i\) 的乘法逆元, 记作 \(t_i\),记 \(\frac{B}{b_i}\)\(B_i\)

于是就可以从 \(a_i \equiv a_i \pmod {b_i}\) 推到 \(a_i \times B_i \times t_i \equiv a_i \pmod {b_i}\)

因为 \(m\) 相互互质,于是 \(\forall i, j\in{[1,n]},i\neq j\) ,有 \(a_i\times B_i\times t_i \equiv 0 \pmod{m_j}\)

于是对于 \(x=\sum^{n}_i a_i \times \frac{B}{b_i} \times inv(\frac{B}{b_i})\) 满足

\(\forall i \in [1, n],x = a_i \times t_i \times B_i + \sum_{j\neq i}{a_j\times t_j \times B_j}=a^i+\sum_{j\neq i} 0 \equiv a_i \pmod {m_i}\) 。得证。

扩展中国剩余定理

和中国剩余定理没什么鸟关系,,,

没想到了扩展中国剩余定理本质是一个暴力啊。

直接讲了,就是两个两个的合并模方程就完了, 推导一下

\[\left\{ \begin{array}{lcl} x \equiv a_1 \pmod {b_1}\\ x \equiv a_2 \pmod {b_2}\\ \end{array} \right. \]

显然通过我前面写的某些东西可以得到这样的方程组:

\[\left\{ \begin{array}{lcl} x = a_1 + b_1 \times k_1\\ x = a_2 + b_2 \times k_2 \end{array} \right. \]

然后写成一个等式就是 \(a_1 + b_1 \times k_1 = a_2 + b_2 \times k_2\) 再表示一下就是

\(b_1\times k_1+(-b_2)\times k_2=a_2-a_1\)

\(d = \gcd(b_1, b_2)\),上面的式子同时除以一个 \(d\)

\(\frac{b_1}{d}\times k_1 - \frac{b_2}{d}\times k_2= \frac{a_2-a_1}{d}\)

于是又可以化成一个模方程

\(\frac{b_1}{d}\times k_1 \equiv \frac{a_2-a_1}{d} \pmod{\frac{b_2}{d}}\)

然后两边同时乘以个 \(\frac{b_1}{d}\) 的在模 \(\frac{b_2}{d}\) 意义下的逆元,就是

\(k_1 \equiv inv(\frac{b_1}{d}) \times \frac{a_2-a_1}{d} \pmod{\frac{b_2}{d}}\),

接着化成等于的形式就是:(不知道为什么我划化来化去的

\(k_1 = inv(\frac{b_1}{d})\times \frac{a_2-a_1}{d} + \frac{b_2}{d} \times y\),然后我们再把 \(k_1\) 带进原式中。

\(x = a_1 + b_1 \times (inv(\frac{b_1}{d})\times \frac{a_2-a_1}{d} + \frac{b_2}{d} \times y)\)

\(x = a_1 + b_1\times inv(\frac{b_1}{d})\times \frac{a_2-a_1}{d} + \frac{b_1\times b_2}{d}\times y\),最后的最后就是

\(x \equiv a_1 + b_1\times inv(\frac{b_1}{d})\times \frac{a_2-a_1}{d} \pmod{\frac{b_1\times b_2}{d}}\)

可以参照之前的一些东西 \(\frac{b_1\times b_2}{\gcd(b_1, b_2)} = lcm(b_1, b_2)\)

\(x \equiv a_1 + b_1\times inv(\frac{b_1}{d})\times \frac{a_2-a_1}{d} \pmod{lcm(b_1, b_2)}\)

就合并成功了。 用 exgcd 求解就好了。

int n;
i64 a[100010], b[100010];
i64 exCRT() {
    i64 a1 = a[0], b1 = b[0];
    i64 x, y;
    for (int i = 1; i < n; i++) {
        __int128 a2 = a[i], b2 = b[i];
        __int128 d = gcd(b1, b2), g = a2 - a1;
        exgcd(b1, b2, x, y);
        if (g % d) {
            return -1ll;
        }
        __int128 md = b2 / d;
        x = (__int128)(x * g / d % md + md) % md;
        a1 = __int128(x * b1 + a1);
        b1 = lcm(b1, b2);
        a1 %= b1;
    }
    return a1 % b1;
}

posted @ 2022-07-09 19:04  落花月朦胧  阅读(105)  评论(5编辑  收藏  举报