中国剩余定理

中国剩余定理

构造法(只适用模数两两互质的情况,CRT的本质思想)

  1. 对于解线性同余方程组 useless,因为完全可以被增量法代替

  2. 但是揭示了若模数两两互质,则线性同余方程组一定有解。

    若模数为合数 \(q\), \(q=p_1^{e_1}*p_2^{e_2}*p_3^{e_3}*...p_k^{e_k}\)

    1. \(x\mod q = a\iff x \mod p_i^{e_i}=a \;同时成立\)

    2. 若给出一个模数不一定为质数线性方程组,判断是否有解,可将每个模数拆成 素数幂 的形式

      对于底数相同的方程分到一组,可先假设幂最高的成立,这样就很容易检查幂低的是否矛盾,若有矛盾则无解

      因为此时每一组之间的模数都是两两互质的,所以只要组中没有矛盾,则该方程组一定有解

中国剩余定理2 - 题目 - Daimayuan Online Judge

性质 2.2 的应用

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <map>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 1e5 + 10;
//[p, [s, a]] 为该组以p为底,p的幂为s, x mod s = a 
map<int, vector<PII> > mp;
bool solve()
{
	int n;
	scanf("%d", &n);
	mp.clear();
	while(n--)
	{
		int a, m;
		scanf("%d%d", &a, &m);
		for (int i = 2; i <= m / i; i++)
		{
			if (m % i)
				continue;
			int s = 1;
			while(m % i == 0)
			{
				s *= i;
				m /= i;
			}
			mp[i].push_back({s, a % s});
		}
		if (m > 1)
			mp[m].push_back({m, a % m});
	}
	for (auto t : mp)
	{
		int p = t.first;
		//找到该组幂最大的
		int now = max_element(mp[p].begin(), mp[p].end())->second;
		for (auto i : mp[p])
		{
			if (now == -1)
				now = i.second;
			if (now % i.first != i.second)
				return false;
		}
	}
	return true;
}
int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
	{
		if (solve())
			puts("Yes");
		else
			puts("No");
	}
	return 0;
}

增量法 (适用于模数不一定两两互质的情况)

给定 \(n\) 个方程,求 \(x\) ,满足 \(x\equiv a_i\;(mod \;m_i)\), 且 \(0<=x<lcm(m_1,m_2...m_n)\)

可逐一合并两个同余方程,最后剩下的同余方程 \(x \equiv a \;(mod \;lcm(m_1,m_2...m_n))\)\(a\) 就是答案

合并两个同余方程的过程为:

\[\begin{aligned} &1.\\ &x\equiv a\;(mod\;b)\\ &x\equiv c\;(mod\;d)\\ &x=b*t+a,\;所以解出\;t\;即可\\ &带入第二个同余方程得:b*t\equiv c-a\;(mod\;d)\\ &令\;g=exgcd(b,d)\\ &若\;g\nmid c-a,\; 则\;该同余方程组无解\\ &2.\\ &否则在\;g=exgcd(b,d)\;中解得的\;t_0\;为\;b*t+d*s=g\;的一个\;t\;的解\\ &令\;d'=\frac dg\;则通解\;t=t_0+d'*y\;(y\;为常数)\\ &此时找到最小的一个非负整数解\;t_0,即若\;t_0<0\;则\;t_0=t_0+d'\\ &3.\\ &令\;t_0=t_0*\frac {c-a}g,\;此时\;t_0\;为\;b*t+d*s=c-a\;的一个解\\ &即\;t_0\;为\;b*t\equiv c-a\;(mod\;d)\;的解\\ &通解仍为\;t=t_0+d'*y\;(y\;为常数)\\ &4.\\ &x=b*t+a=b*(t_0+d'*y)+a=b*d'*y+b*t_0+a\\ &因此\;x\equiv b*t_0+a\;(mod\;b*d') \end{aligned} \]

中国剩余定理 - 题目 - Daimayuan Online Judge

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
typedef long long ll;

ll exgcd(ll a, ll b, ll &x, ll &y)
{
	if (b == 0)
	{
		x = 1, y = 0;
		return a;
	}
	ll xx, yy;
	ll d = exgcd(b, a % b, xx, yy);
	x = yy, y = xx - a / b * yy;
	return d;
}

void merge(ll &a, ll &b, ll c, ll d) // d <= 1e9, b可能很大
{
	// bt = c - a (mod d)
	if (a == -1 && b == -1)
		return;
	ll x, y;
	ll g = exgcd(b, d, x, y);
	if ((c - a) % g != 0)
	{
		a = b = -1;
		return;
	}
	d /= g; // d'
	ll t0 = ((c - a) / g) % d * x % d; // 注意 c - a 可能很大,及时取模
	//t = t0 (mod d')
	if (t0 < 0) t0 += d; // 若为负数则 + 模数 就是最小非负整数
	a = b * t0 + a;
	b = b * d;
}

void solve()
{
	int n;
	scanf("%d", &n);
	ll a = 0, b = 1; //当前的方程为 x mod b = a,每次迭代合并一个 x mod c = d
	for (int i = 1; i <= n; i++)
	{
		ll c, d;
		scanf("%lld%lld", &c, &d);
		merge(a, b, c, d);
	}
	printf("%lld\n", a);
}
int main()
{
	int T;
	scanf("%d", &T);
	while(T--)
		solve();	
	return 0;
}

并且中国剩余定理可以推出:

\(q=p_1^{e_1}*p_2^{e_2}*p_3^{e_3}*...p_k^{e_k}\)

$

posted @ 2022-05-19 19:57  hzy0227  阅读(72)  评论(0编辑  收藏  举报