中国剩余定理学习总结

中国剩余定理学习总结

理解:

     m1m2m3...mk是两两互素的正整数,即gcd(mi,mj)=1i!=ji,j=1,2,3,...,k.

则同余方程组:

x = a1 (mod n1)

x = a2 (mod n2)

...

x = ak (mod nk)

(n1,n2,...nk)有唯一解,即在(n1,n2,...,nk)的意义下,存在唯一的x,满足:

x = ai mod (n1, n2,...,nk),i = 1, 2, 3, ..., k

解可以写为这种形式:

ðx = sigma (a* mi* mi') mod(N)

其中N = n* n* ... * nk mi= N / nimi'mi在模ni乘法下的逆元。

思想:

通过上面的x的求法,可以比较明确的看出,我们针对一个ax0当我们对于x % nx0时,显然,对于对应的ax0* mx0 * mx0',mx0'是对于mx0对于mod nx0的逆元,所以,显然对于mx0 * mx0' % nx00,然后针对其他的项,因为所有的ni互质,所以其他项,均mod0,只有当前还有一个a,得证。

Source:

/*O(n log n) 中国剩余定理(crt)*/
int CRT(int *res, int *mod, int n) {
	int ans = 0, m = 1;
	for (int i = 1; i <= n; ++i) m *= mod[i];
	for (int i = 1; i <= n; ++i) {
		int mi = m / mod[i];
		ext_gcd(mi, mod[i], x, y);
		ans = (ans + mi * x * res[i]) % m;
	}
	if(ans < 0) ans += m;
	return ans;
}

拓展:不互质形式

因为对于这种普通的中国剩余定理,我们必须要求要对每一个mod都保证两两互质,这样才能直接使用,但是现实是不是每一道题都能直接满足,所以我们应该要做到有通解的方法。

烤炉我们需要求取满足

m = a1 (mod r1)

m = a2 (mod r2)

...

m = ak (mod rk)

的一个x,那么我们提出任意两个等式

m = a1 * x + r1 = a2* y + r2

ð a1 * x + r1 = a2 * y+ r2

ð a1 * x + a2 * (-y) =r2 – r1

ð 形如 a * x + b * y = c的形式

那么直接通过扩欧就可以确定当前能取得的最小的x

所以,针对这两个等式所求的m即为,m = a1 * x + r1那么詪显然的,为了满足继续的等式,并且不破坏当前的性质,即m’ % a1 = n1

m’ % a2 = r2 那么我们只要满足 (m’ – m) % [a1, a2]= 0 即可,所以对于这两个等式我们可以合并成为m’ = m (mod [a1, a2])

也就可以变形成为 m’ = [a1, a2] * x +m è 形如原来的等式m = a * x + r, 然后往后面依次计算即可。每一次联立两个等式,进行扩欧,最后得到满足所有等式的答案。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <cctype>

using namespace std;

inline void R(long long &v) {
	char c = 0;
	bool p = true;
	v = 0;
	while(!isdigit(c)) {
		if(c == '-')
			p = false;
		c = getchar();
	}
	while(isdigit(c)) {
		v = (v << 3) + (v << 1) + (c ^ '0');
		c = getchar();
	}
	if(!p)
		v = -v;
}

long long s, t, v, e, x, y, a, b, c;
long long ans;

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

void ext_gcd(long long a, long long b, long long &x, long long &y) {
	b ? (ext_gcd(b, a % b, y, x), y -= a / b * x) : (x = 1, y = 0);
}

long long n, a1, r1, a2, r2;

int main() {
	while(scanf("%lld", &n) != EOF) {
		bool flag = true;
		R(a1), R(r1);
		for(int i = 1; i < n; ++i) {
			/*因为不一定满足所有的mod都是互质的
			所以我们不能用普通的CRT解决,*/
			R(a2), R(r2);
			a = a1, b = a2, c = r2 - r1;
			long long t = gcd(a, b);
			if(c % t) { flag = false; continue; }
			a /= t, b /= t, c /= t;
			ext_gcd(a, b, x, y);
			x = (x % b * c % b + b) % b;
			/*针对任意两个算式,求取m = a1 * x + r1 = a2 * y + r2
			即, a1 * x - a2 * y = r2 - r1 ==> ax + by = c
			直接用扩欧解决即可, 然后对于求得的答案,因为要与下一个、
			联立, 那么只要满足,新的 m' = m + C * [a1, a2]即可
			然后又可以将算式,转变为m = a1 * x + r1的形式继续联立*/
			long long temp = a1;
			a1 = t * a * b;
			a = x * temp + r1;
			r1 = a;
		}
		if(!flag) cout << "-1" << '\n';
		else cout << a << '\n';
	}
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值