中国剩余定理CRT及 扩展中国剩余定理扩展CRT
Posted 。✧* ꧁王者꧂✧*
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中国剩余定理CRT及 扩展中国剩余定理扩展CRT相关的知识,希望对你有一定的参考价值。
CRT(中国剩余定理)
据说,这是唯一一个以中国来命名的定理。又叫孙子定理。
用现代数学的语言来说明的话,中国剩余定理给出了以下的一元线性同余方程组:(S中m i与m j互质)
求一个满足集合S的通解X;
想必电脑前的你满脸都是疑惑(也可能只是我太蒟蒻。。。)
对于S1(即S集合中的第一项式子),x=a1+k1*m1;
对于S2,x=a2+k2*m2;
S3,x=a3+k3*m3;…
对于X,要使X%m1=a1,X%m2=a2,X%m3=a3,所以,X的表达式里,a1到an一定都会出现,且满足条件。
那么,我们如何构造这样一个数X呢?
千万不要忘记题目条件,mi与mj互质。(!!!)
根据上述式子,我们可以让
X-a1=k1*m1,
X-a2=k2*m2,
X-a3=k3*m3…
为了满足X%m1=a1,我们可以使a2到an都乘上m1;
对于X%m2=a2,我们可以使除a2外的a都乘m2…
这样,利用归纳法,可以求出X的一个特值:
使M为所有m相乘的结果,M1=M/m1,M2=M/m2…以此类推
再求出每个Mi模mi意义下的逆元ti(专业术语不会打,且将就看看吧),
就得到一个X的解:X=M1 * t1 *a1+M2 * t2 * a2 + … Mn * tn * an;
试着把这个解代入方程集,是不是会奇妙地发现,它对了。
推荐一道题:曹冲养猪(模板题)
代码!!!
:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,yu[11],m[11],N=1,M[11],x,y,t[11],qwq,ans;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0) { x=1,y=0;return a;}
qwq=exgcd(b,a%b,x,y);
ll z=x;
x=y,y=z-a/b*y;
return qwq;
}
int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++) scanf("%lld%lld",&m[i],&yu[i]),N*=m[i];
for(ll i=1;i<=n;i++) M[i]=N/m[i];
for(int i=1;i<=n;i++)
{
exgcd(M[i],m[i],x,y);
t[i]=x;
t[i]=(t[i]%m[i]+m[i])%m[i];//求逆元
ans+=yu[i]*t[i]*M[i];
}
printf("%lld",(ans%N+N)%N);
return 0;
}
扩展CRT(扩展中国剩余定理)
请看,这张图片,是不是很熟悉呢。
毫无疑问,它与扩展CRT的图片一模一样(因为我用的两张图片一样/233)。
但是,它们的不同之处就在于,扩展CRT中的m没有互质这个限制。
请大家思考一下,在普通CRT中我们会求出一组M和每个M在mod m情况下的逆元t,逆元存在的前提是M与m互质,若m之间不互质,那么M就不与m互质,也就是说,我们无法找到M在mod m下的逆元,这样也就满足不了题目要求,所以,为了解决这个问题,便有了扩展CRT。
首先,我们来考虑两个式子时,能得到的x:
x≡r1(mod m1),x≡r2(mod m2)
我们可以利用二元一次方程的想法:
设未知数k1与k2,使:k1 * m1 +r1= k2 * m2 +r2。
将未知数移到左边,得:k1 * m1-k2 * m2=r2 - r1。
看到这个式子,你有没有想到什么呢???
这样,扩展欧几里得(bezout定理)就派上用场啦!!!
ax + by = c, a变为m1 , b变为m2 , x 变为 k1, y 变为 k2。
m1 * x(k1) + m2 * y(k2) = c(r2 -r1)。
那么,我们就可以求出一组 k1 与 k2 的特值。
。
那我们的x不就求出来了吗:x=r1 + k1 * m1.(把求得的k1代入x的表达式中)
同时,我们还要再求一下x的最小非负整数解,这在之前已经讲过了。
紧接着,再考虑x的通解 :x = xo + k * lcm(m1 , m2)。(k为任意实数,xo是满足x这个集合的任意解,xo+k * lcm(m1,m2),是因为我们需要加上的这个数,既需要被m1整除,也需要被m2整除)。
再继续想一下,这个式子,我们可不可以,将它变为 x=r(mod m)这样的类型呢?
如果可以,那我们就相当于将 两个式子合并,这样就为 继续求解下面的满足x的式子提供了条件:
x≡xo( mod lcm(m1,m2) )。
这样,是不是有思路了呢!!
总结一下,我们在写扩展CRT这类题目的时候,要将两个式子一个一个进行合并,最后得到满足所有x的解。
推荐题目:Strange Way to Express Integers
上代码:
#include<bits/stdc++.h>
using namespace std;
#define re register
typedef long long ll;
const int N=1e5+10;
int n;
ll k1,k2,r[N],m[N],M,R;
ll mrgcd(ll a,ll b) //扩展欧几里得求k1,k2.
{
// if(a<b) swap(a,b);
if(b==0)
{
k1=1,k2=0;
return a;
}
ll gcd=mrgcd(b,a%b);
ll z=k1;
k1=k2,k2=z-a/b*k2;
return gcd;
}
ll kuai(ll a,ll b,ll p)
{
ll ans = 0;
while(b>0){
if(b&1)
ans = (ans + a) % p;
a = (a << 1) % p;
b >>= 1;
}
return ans;
}
void work()
{
R=r[1],M=m[1];//R,M分别记录的是合并后得到的r,m,即:x≡R(mod M)
for(re int i=2;i<=n;i++)
{
ll c=((r[i]-R)%m[i]+m[i])%m[i];//此处理是为了使c为正数,避免c因为是负数而造成对结果的影响。,本质为将ax+by=c化为 ax≡c(mod b)。
ll gc=mrgcd(M,m[i]);
if(c%gc)
{
R=-1;
printf("-1");
break ;
}
ll q1=c/gc,q2=m[i]/gc;
k1=(kuai(k1,q1,q2)+q2)%q2;//求k1的最小非负整数解
R=R+k1*M;
M=M/gc*m[i];//对M值进行更新
R=((R%M)+M) % M;//对R值进行更新
}
}
int main()
{
freopen("strange.in","r",stdin);
freopen("strange.out","w",stdout);
scanf("%d",&n);
for(re int i=1;i<=n;i++)
scanf("%lld%lld",&m[i],&r[i]);
work();
if(R!=-1)
printf("%lld",R);
return 0;
}
以上是关于中国剩余定理CRT及 扩展中国剩余定理扩展CRT的主要内容,如果未能解决你的问题,请参考以下文章