中国剩余定理

“有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?”——《孙子算经》

中国剩余定理

即求多个同余方程的解。

举个栗子。求一个数x,使得:

{x2(mod3)x3(mod5)x2(mod11)

设M=3511=165, a1=3,a2=5,a3=11,

M1=M/a1=55

M2=M/a2=33

M3=M/a3=15

然后我们再求a的逆元c。(扩展欧几里得)

然后结果就是(a1M1c1 + a2M2c2 + a3M3c3)%M

当然,我们要求的不仅仅是符合3个数的要求。于是我们就可以得到代码:

(变量名和以上有出入)

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
    if(!b)d=a,x=1,y=0;
    else{
        exgcd(b,a%b,d,y,x);
        y-=(a/b)*x;
    }
}

ll China(int n,ll *m,ll*a)
{
    ll M=1,d,y,x=0;
    for(int i=0;i<n;i++)M*=m[i];
    for(int i=0;i<n;i++){
        ll w=M/m[i];
        exgcd(m[i],w,d,d,y);
        x=(x+y*w*a[i])%M;
    }
    return (x+M)%M;
}
ll m[15],a[15];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    scanf("%lld%lld",&m[i],&a[i]);
    printf("%lld",China(n,m,a));
    return 0;
}

以上是中国剩余定理的内容(CRT)

扩展中国剩余定理(exCRT)

简单来说就是对于每两个同余方程,我们可以把它们合并成一个同余方程,并在每次计入一个新的同余方程的时候得到新的解。我们理性地思考一下,如:

{xy1(modm1)xy2(modm2)...xyn(modmn)

M=LCMi1k1mi

其中m1,m2,m3...mn为不一定两两互质的整数,求x的最小非负整数解.
假设前n-1项已经求出解为x,那么对于第n个式子:

x+tMyn(modmn)

M和yn、mn是已知的,我们只需exgcd求出x和t就行了。


或者我们把合并感性理解一下,假设原来只有一个同余方程

xy1(modm1)x+k1m1=y1

我们再加入一个方程

xy2(modm2)x+k2m2=y2

两者相减,得到

k1m1k2m2=y1y2

再用exgcd解得k1k2即可。那么要求x的值回代即可。

那么对于一个新加入的式子就有:

k1m1+y1+tm3=k3m3+y3

(k1、m1和y1表示上次的x,用②式表示也可以)发现新的未知数是t和k3,重新用exgcd求解即可。

当然这里只写了三个式子。想要更多同余方程组的解?请多次exgcd。这个思维和上面的思维是等价的。
洛谷P4777

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cctype>
using namespace std;
typedef long long ll;
inline ll read()
{
	bool w=0;ll x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const ll maxn=1e5+100;
	ll y[maxn],m[maxn],n;
	inline void exgcd(ll a,ll b,ll &x,ll &y,ll &d){
		if(!b)d=a,x=1,y=0;
		else exgcd(b,a%b,y,x,d),y-=x*(a/b);
	}
	ll mul(ll a,ll b,ll mod){
		ll res=0;
		while(b){
			if(b&1)res=(res+a)%mod;
			a=(a+a)%mod;
			b>>=1;
		}
		return res;
	}
	inline ll excrt()
	{
		ll xx,yy,k;
  		ll M=m[1],ans=y[1];
		for(int i=2;i<=n;i++)
    	      {
			ll a=M,b=m[i],c=(y[i]-ans%b+b)%b;//x≡y(mod m),ans为上一次的解。
   			ll gcd;
			exgcd(a,b,xx,yy,gcd);//tM+km=y-x,c是常数项,xx和yy分别表示t和k
			ll bg=b/gcd;//这个是为了exgcd的最后一个步骤,求解出最小非负整数解
   			if(c%gcd!=0) return -1;
			xx=mul(xx,c/gcd,bg);
			ans+=xx*M;
			M*=bg;//M为前k个m的lcm
 			ans=(ans%M+M)%M;
		}
		return (ans%M+M)%M;
	}
	inline void cried()
	{
		n=read();
		for(int i=1;i<=n;i++)m[i]=read(),y[i]=read();
		printf("%lld\n",excrt()); 
	}
}
int main()
{
	star::cried();
	return 0;
}
posted @   Star_Cried  阅读(156)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示