中国剩余定理 学习笔记
中国剩余定理 (Chinese Remainder Theorem) 简称CRT。
CRT
CRT可以求解一个这样的方程组的最小解
这里保证数组 两两互质。
CRT的原理就是得到 使 并且
显然答案就是
算法流程如下:
- 计算
- 对于第 个方程:
a. 计算
b. 计算 在模 的逆元
c. 计算 - 方程的解为
证明略 (显然啊)
代码(洛谷P1495):
#include<cstdio> #define maxn 100039 using namespace std; //#define debug typedef long long ll; typedef long long Type; inline Type read(){ Type sum=0; int flag=0; char c=getchar(); while((c<'0'||c>'9')&&c!='-') c=getchar(); if(c=='-') c=getchar(),flag=1; while('0'<=c&&c<='9'){ sum=(sum<<1)+(sum<<3)+(c^48); c=getchar(); } if(flag) return -sum; return sum; } ll n,a[maxn],b[maxn]; ll sn,m,c,ans; ll gcd(ll a,ll b){ if(a%b==0) return b; return gcd(b,a%b); } inline ll lcm(ll a,ll b){ return a*b/gcd(a,b); } void exgcd(ll a,ll b,ll &x,ll &y){ if(!b){ x=1; y=0; return; } exgcd(b,a%b,x,y); ll t=x; x=y; y=t-a/b*y; return; } ll js(ll a1,ll b1,ll a2,ll b2){ ll p,q; exgcd(a1,-a2,p,q); if((b2-b1)%gcd(a1,-a2)) return -1; q*=(b2-b1)/gcd(a1,-a2); p*=(b2-b1)/gcd(a1,-a2); //if(a1*p+b1 != a2*q+b2) printf("Oops!\n"); //else printf("OK\n"); int r=lcm(a1,a2); return ((a2*q+b2)%r+r)%r; }//x=b_i(mod a_i) ll excrt(){ ll ta,tb; ta=a[1],tb=b[1]; for(int i=2;i<=n;i++){ tb=js(ta,tb,a[i],b[i]); if(tb==-1) return -1; ta=lcm(ta,a[i]); } return tb; } int main(){ //freopen("1.in","r",stdin); //freopen("my.out","w",stdout); n=read(); sn=1; for(int i=1;i<=n;i++) a[i]=read(),b[i]=read();//x=b_i(mod a_i) printf("%lld",excrt()); return 0; }
exCRT
如果数组 不是两两互质呢?
我们尝试修复一下CRT的方法让它变成exCRT,但是我们发现,CRT的核心都说过了,它是求出 使 并且 ,但是我们发现,我们有时不能找到这样的 ,所以CRT原来的思路是不能实现的。
看来只能用另外一种方法了。
考虑两个方程的情况。
令
移项,得到
显然只要exgcd解出 的一组解就可以解决问题了,然后显然合并后的方程是:
然后就可以过 洛谷P4777了,当然要注意精度问题,加上防爆乘,不然会炸精度。。。
#include<cstdio> #define maxn 100039 using namespace std; //#define debug typedef long long ll; typedef long long Type; inline Type read(){ Type sum=0; int flag=0; char c=getchar(); while((c<'0'||c>'9')&&c!='-') c=getchar(); if(c=='-') c=getchar(),flag=1; while('0'<=c&&c<='9'){ sum=(sum<<1)+(sum<<3)+(c^48); c=getchar(); } if(flag) return -sum; return sum; } ll mol(ll a,ll b,ll p){ int flag=0; if(a<0) a=-a,flag^=1; if(b<0) b=-b,flag^=1; ll ans=0,tmp=a; while(b){ if(b&1) ans=(ans+tmp)%p; b>>=1; tmp=(tmp<<1)%p; } if(flag) return (-ans%p+p)%p; return ans%p; } ll gcd(ll a,ll b){ if(a%b==0) return b; return gcd(b,a%b); } inline ll lcm(ll a,ll b){ return a/gcd(a,b)*b; } void exgcd(ll a,ll b,ll &x,ll &y){ if(!b){ x=1; y=0; return; } exgcd(b,a%b,x,y); ll t=x; x=y; y=t-(a/b)*y; return; } int n; ll a[maxn],b[maxn]; ll excrt(){ ll m=a[1],ans=b[1]; ll p,q; for(int i=2;i<=n;i++){ exgcd(m,a[i],p,q); //if((b[i]-ans)%gcd(m,a[i])!=0) return -1; p=mol(p,(b[i]-ans)/gcd(m,a[i]),a[i]/gcd(m,a[i])); ans=(mol(p,m,lcm(a[i],m))+ans)%lcm(a[i],m); m=lcm(a[i],m); } return ans%m; }//x=b(mod a) int main(){ //freopen("P4777_13.in","r",stdin); //freopen(".in","w",stdout); n=read(); for(int i=1;i<=n;i++) a[i]=read(),b[i]=read(); printf("%lld",excrt()); return 0; }
分类:
笔记
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具