中国剩余定理

详细介绍+理解

写的非常好,一看就懂。
《孙子算经》问题简化:

  • 已知 n%3=2, n%5=3, n%7=2, 求n。

“中国剩余定理”具体解法分三步:

注意定理中三个数两两互质

  • 从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
    即满足:
    lcm(3,5)%7=1 的最小数 15
    lcm(3,7)%5=1 的最小数 21
    lcm(5,7)%3=1 的最小数 70
  • 用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加。
    即:
    (15x2+21x3+70x2)=233
  • 用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。
    即:
    n =233%lcm(3,5,7) = 23 这是n的最小解
中国剩余模版(互质)
ll extend_gcd(ll a, ll b, ll &x, ll &y){
	ll res=a;
	if (b!=0){
		res=extend_gcd(b,a%b,y,x);
		y-=(a/b)*x;
	}
	else {
		x=1;
		y=0;
	}
	return res;
}
// / m[i] 余 a[i]
ll china(int len, ll *m, ll *a){
	ll M=1,w,d,x,y,ret=0;
	for (int i=0; i<len; i++) M*=m[i];
	for (int i=0; i<len; i++){
		w=M/m[i];
		d=extend_gcd(m[i],w,x,y);
		ret=(ret+y*w*a[i])%M;
	}
	return (M+ret%M)%M;
}
扩展中国剩余模版(不互质)
ll m[maxn],a[maxn];
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}

ll extend_gcd(ll a, ll b, ll &x, ll &y) {
	ll res=a;
	if (b!=0) {
		res=extend_gcd(b,a%b,y,x);
		y-=(a/b)*x;
	} else {
		x=1;
		y=0;
	}
	return res;
}

ll exchina(ll n) {
	ll m1=m[0],a1=a[0];
	ll m2,a2,k1,k2,x0,g,c;
	ll lcm=m[0];
	for(int i=1; i<n; i++) {
		m2=m[i];
		a2=a[i];
		c=a2-a1;
		g=extend_gcd(m1,m2,k1,k2);
		lcm=lcm*m[i]/gcd(lcm,m[i]);
		if(c%g) return -1; //c如果不是g的倍数,则无解
		x0=k1*c/g;	
		ll t=m2/g;
		x0=(x0%t+t)%t;	//保证x0为最小正数  c/t是余数,系数扩大余数倍
		a1+=m1*x0;
		m1=t*m1;
	}
	if (a1==0){ //余数为0,说明m[]是等比数列。且余数都为0
		a1=1;
		for (int i=0; i<n; i++)
			a1=a1*m[i]/gcd(a1,m[i]);
	}
	return a1;
}
应用
POJ1006 AC代码

中国剩余定理

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;

ll a[4];
ll m[4]={23,28,33};
const int mod=21252;
ll extend_gcd(ll a, ll b, ll &x, ll &y) {
	ll res=a;
	if (b!=0) {
		res=extend_gcd(b,a%b,y,x);
		y-=(a/b)*x;
	} else {
		x=1;
		y=0;
	}
	return res;
}

ll china(int len, ll *m, ll *a) {
	ll M=1,w,d,x,y,ret=0;
	for (int i=0; i<len; i++) M*=m[i];
	for (int i=0; i<len; i++) {
		w=M/m[i];
		d=extend_gcd(m[i],w,x,y);
		ret=(ret+y*w*a[i])%M;
	}
	return (M+ret%M)%M;
}


int main() {

	//ios::sync_with_stdio(0);
	ll d;
	int cnt=1;
	while (cin>>a[0]>>a[1]>>a[2]>>d) {
		if(a[0]==-1 && a[1]==-1 && a[2]==-1 && d==-1)
			break;
		ll ans=(china(3,m,a)-d)%mod;
		if (ans<=0) ans+=mod;
		printf("Case %d: the next triple peak occurs in %lld days.\n",cnt++,ans);
	}
	return 0;
}
POJ2891 AC代码

扩展中国剩余定理

#include <iostream>
#include <cstdio>

typedef long long ll;
using namespace std;


ll m[60000],a[60000];
ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}

ll extend_gcd(ll a, ll b, ll &x, ll &y) {
	ll res=a;
	if (b!=0) {
		res=extend_gcd(b,a%b,y,x);
		y-=(a/b)*x;
	} else {
		x=1;
		y=0;
	}
	return res;
}

ll exchina(ll n) {
	ll m1=m[0],a1=a[0];
	ll m2,a2,k1,k2,x0,g,c;
	ll lcm=m[0];
	for(int i=1; i<n; i++) {
		m2=m[i];
		a2=a[i];
		c=a2-a1;
		g=extend_gcd(m1,m2,k1,k2);
		lcm=lcm*m[i]/gcd(lcm,m[i]);
		if(c%g) return -1;
		x0=k1*c/g;
		ll t=m2/g;
		x0=(x0%t+t)%t;
		a1+=m1*x0;
		m1=t*m1;
	}
	if (a1==0){
		a1=1;
		for (int i=0; i<n; i++)
			a1=a1*m[i]/gcd(a1,m[i]);
	}
	return a1;
}


int main() {

	ios::sync_with_stdio(0);
	ll n;
	while (cin>>n) {
		for (int i=0; i<n; i++) {
			cin>>m[i]>>a[i];
		}
		if (n==1) {
			cout<<a[0]<<endl;
			continue;
		}
		ll ans=exchina(n);
		cout<<ans<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值