CRT&EXCRT(中国剩余定理和扩展中国剩余定理)

稍微难一点,其实也挺简单。

CRT:

用途:

给定一个同余方程组,保证所有 m 两两互质:

{xa1(modm1)xa2(modm2)...xan(modmn)

用于求其解。

具体方法:

自我感觉叫方法好一点,建议理解记忆,公式见下。
首先我们先找一组数 n ,使得:

nimodmi=ai 或者说 niai(modmi)

因为:如果 amodb=t 那么 (a+k×b)modb=t (比较显然
所以:
如果 m1n2 那么 (n1+n2)modm1=a1
如果 m2n1 那么 (n1+n2)modm2=a2
也就是说,要想使

{n1+n2a1(modm1)n1+n2a2(modm2)

只需要

{m1n2m2n1

同理,我们设 sum=i=1nni,M=i=1nmi 要想使:

{suma1(modm1)suma2(modm2)...suman(modmn)

只需要

{m2,m3,...,mnn1m1,m3,...,mnn2...m1,m2,...,mn1nn

因为所有 m 两两互质,所以只需要

M÷mini

接下来考虑怎么求 ni
我们设 ni=M÷mi×ti
于是我们只需求一个 ti ,使其满足

M÷mi×tai(modmi)

因为 M÷mi 是个常数,所以直接用 exgcd求解即可。
当然也可以先用乘法逆元算 M÷mi×t1(modmi) 在乘上 ai ,本质相同。

这样我们就可以求出所有 ni ,进而求出 x=i=1nni,如果 x 要求最小,只需要 modM 即可。

公式:

M=i=1nmi,Mi=M÷mi

x=i=1nai×Mi×Mi1modM

这里的 Mi1Mi 对于 mi 的逆元,不能和 Mi 合并。

例题

CODE(点击查看)
#include<bits/stdc++.h>
using namespace std;
#define llt long long
int n,m[12],a[12];
template<typename T>
inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
void exgcd(llt a,llt b,llt &x,llt &y){
if(b==0) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
int main(){
read(n);
long long tm=1,ans=0;
for(int i=1;i<=n;i++) read(m[i]),read(a[i]),tm*=m[i];
for(int i=1;i<=n;i++){
long long lm=tm/m[i],x,y;
exgcd(lm,m[i],x,y);
x=(x%m[i]+m[i])%m[i];
ans=(ans+x*a[i]*lm)%tm;
}
printf("%lld",ans%tm);
}

这里推荐一篇博客,讲的很细(我从那里学的,本文也有很多借鉴。

EXCRT

其实和 crt 没有什么关联,单纯推式子。
我们来看一下同余方程组

{xa1(modm1)xa2(modm2)...xan(modmn)

这里不保证 m 互质
首先看第一二个式子:

{xa1(modm1)xa2(modm2)

变形得到:

{x=a1+m1k1x=a2+m2k2

a1+m1k1=a2+m2k2

整理:

m1k1m2k2=a2a1

运用 exgcd 解得 k1 的一组解:

k1=r

k1 的通解为 k1=r+m2gcd(m1,m2)×ttZ

将上式带入 x=a1+m1k1 得:

x=a1+m1r+m1m2gcd(m1,m2)×t

lcm(m1,m2)=m1m2gcd(m1,m2)

x=a1+m1r+lcm(m1,m2)×t

变形得到:

xa1+m1r(modlcm(m1,m2))

于是我们就成功将两个同余方程化简成了一个。
同理化简下去直到一个,求解即可。

例题

CODE(点击查看)
#include<bits/stdc++.h>
using namespace std;
#define llt long long
__int128 n,na=0,nm=1;
template<typename T>
inline void read(T &x){
char s=getchar();x=0;bool pd=false;
while(s<'0'||'9'<s){if(s=='-') pd=true;s=getchar();}
while('0'<=s&&s<='9')x=(x<<1)+(x<<3)+(s^48),s=getchar();
if(pd) x=-x;
}
llt gcd(llt a,llt b){return (b==0)?a:gcd(b,a%b);}
llt lcm(llt a,llt b){return a/gcd(a,b)*b;}
void exgcd(__int128 a,__int128 b,__int128 &x,__int128 &y){
if(b==0) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
inline void hebing(llt a,llt m){
llt sa=a-na;
__int128 x,y;
llt gd=gcd(nm,m);
exgcd(nm,m,x,y);
llt lm=m/gd;x*=(sa/gd),y*=(sa/gd);
x=(x%lm+lm)%lm;
na=na+nm*x;
nm=lcm(nm,m);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
read(n);
for(llt a,m,i=1;i<=n;i++) read(m),read(a),hebing(a,m);
__int128 x,y;
exgcd(1,nm,x,y);
x=(x*na%nm+nm)%nm;
long long ans=x;
printf("%lld",ans);
}

参考博客:https://www.cnblogs.com/sparkyen/p/11432052.html

posted @   5k_sync_closer  阅读(66)  评论(1编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示