数论 · 扩展中国剩余定理(EXCRT)

问题

已知有:

{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) ⋯ x ≡ a k ( m o d m k ) \begin{cases}x\equiv{a_1}\pmod{m_1}\\x\equiv{a_2}\pmod{m_2}\\\cdots\\x\equiv{a_k}\pmod{m_k}\end{cases} xa1(modm1)xa2(modm2)xak(modmk)

m m m 数组的数两两不一定不互质的情况下,求 x x x 的最小非负整数解。

求解

假设我们已经求解出了前 ( k − 1 ) (k-1) (k1) 个方程的解为 x x x,且有 M M M L C M i = 1 k − 1 m i LCM_{i = 1}^{k-1} m_i LCMi=1k1mi

此时易知前 k k k 个方程的通解就是 x + t ∗ M x + t* M x+tM t t t 为整)。

现在我们要求解一个 t ′ t' t,使得 x + t ′ ∗ M ≡ a k ( m o d m k ) x + t' * M \equiv a_k \pmod {m_k} x+tMak(modmk)

转换一下就是 m k ∗ r + t ′ ∗ M = a k − x m_k * r +t' * M = a_k - x mkr+tM=akx

exgcd无处不在!!


假设我们现在用扩欧几里得求解方程 a ′ x + b ′ y = c ′ a'x+b'y=c' ax+by=c

那么 a ′ = m k ,   b ′ = M ,   c ′ = a k − x a'=m_k,\ b'=M,\ c'=a_k-x a=mk, b=M, c=akx

然后我们解得 x 0 x_0 x0 满足 a ′ x + b ′ y = gcd ⁡ ( a ′ , b ′ ) a'x+b'y=\gcd(a',b') ax+by=gcd(a,b)

转化一下: x j = x 0 ∗ c ′ / gcd ⁡ ( a ′ , b ′ ) x_j=x_0 * c' / \gcd(a',b') xj=x0c/gcd(a,b)

这样我们就有一个解 x j x_j xj(也就是上柿的 r r r),要求 x m i n x_{min} xmin

详情请戳 ->《数论 · 求解线性同余方程》

t = b ′ gcd ⁡ ( a ′ , b ′ ) t=\dfrac{b'}{\gcd(a',b')} t=gcd(a,b)b,就有 x m i n = ( x j % t + t ) % t x_{min}=(x_j \% t+t)\%t xmin=(xj%t+t)%t

然后再把它统计到 x x x 中,这样 x x x 就是满足前 k k k 个方程的解了。

别忘了也统计到 M M M 里面去。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

#define int long long
#define rint register int
#define fo(i, a, b) for(rint i (a); i <= b; ++i)
const int maxn = 1e5 + 5;

int n, a[maxn], m[maxn];
int x, y;

inline int read ()
{
	int x = 1, s = 0;
	char ch = getchar ();
	while (ch < '0' or ch > '9') {if (ch == '-') x = -1; ch = getchar ();}
	while (ch >= '0' and ch <= '9') s = s * 10 + ch - '0', ch = getchar ();
	return x * s;
}

inline int exgcd (int a, int b)
{
	if (!b)
	{
		x = 1, y = 0;
		return a;
	}
	int res = exgcd (b, a % b);
	int tmp = x;
	x = y, y = tmp - a / b * y;
	return res;
}

inline int pmul (int x, int p, int mod)
{
	int res = 0;
	while (p >= 1)
	{
		if (p & 1)
			res = (res + x) % mod, p -= 1;
		p /= 2, x = (x + x) % mod;
	}
	return res;
}

int M, ans;

inline int excrt ()
{
	M = m[1], ans = a[1];
	fo (i, 2, n)
	{
		int a_ = M, b_ = m[i], c_ = (a[i] - ans % b_ + b_) % b_;
		int gcd = exgcd (a_, b_), bg = b_ / gcd;
		x = pmul (x, c_ / gcd, bg);
		ans = ans + x * M;
		M *= bg, ans = (ans % M + M) % M;
	}
	return ans;
}

signed main ()
{
	n = read ();
	fo (i, 1, n) 
		m[i] = read (), a[i] = read ();
	printf ("%lld\n", excrt ());	
	return 0;
}

—— E n d End End——

posted @ 2022-03-25 07:25  pldzy  阅读(44)  评论(0)    收藏  举报