中国剩余定理和扩展中国剩余定理

中国剩余定理

定理

f(x)={xa1(modm1)xa2(modm2)...xan(modmn)m1,m2,m3...,mn

M=i=1nmi,Mi=Mmi,ti=Mi1ti 是在模 mi 意义下的乘法逆元

则有最小解 x=(i=1naitiMi)%M

证明

简易说明

M=i=1nmi,Mi=Mmi,ti=Mi1x=a1t1M1+a2t2M2+..+antnMn+kM=kM+i=1naitiMi,kZxmin=(i=1naitiMi)%M

证明 i=1naitiMi 是一个解

xai(modmi) x%mi=(a1t1M1+a2t2M2+..+antnMn)%mi=aitiMi%mi+j=1,j!=inajtjMj%mij!=imi|Mj       mxi%mi=aitiMi%mitiMi1(modmi)xi%mi=ai%mixiai(modmi)

证明kM+i=1naitiMi,kZ 是一个解以及最小解

x=kM+i=1naitiMi,kZkM,kZQ(i=1naitiMi)%MM

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn = 423;
ll int_maxn=1e9;
ll ll_maxn=1e18;
const ll mod=1e9+7;
const ll cs=998244353;
inline ll read_int(){
    ll a=0,f=0,g=getchar();
    while(g<'0'||g>'9'){if(g=='-') f=1;g=getchar();}
    while('0'<=g&&g<='9') a=a*10+g-'0',g=getchar();
    return f ? -a : a;
}
inline void write(ll s,bool f){
    int top=0,a[40];
    if(s<0) s=-s,putchar('-');
    while(s) a[++top]=s%10,s/=10;
    if(top==0) a[++top]=0;
    while(top) putchar(a[top]+'0'),top--;
    if(f) putchar('\n');
}

ll n;
ll A[maxn],M[maxn];
ll T[maxn],MM[maxn];
ll lin=1;

inline ll exgcd(ll a,ll b,ll & x,ll & y){
	if(b==0){x=1,y=0;return a;}
	ll gcd=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return gcd;
}

inline void read(){
	n=read_int();
	for(ll i=1;i<=n;i++) M[i]=read_int(),A[i]=read_int(),lin*=M[i];
	for(ll i=1;i<=n;i++) MM[i]=lin/M[i];
	ll ans=0;
	ll x,y;
	for(ll i=1;i<=n;i++) exgcd(MM[i],M[i],x,y),T[i]=(x%M[i]+M[i])%M[i];
	for(ll i=1;i<=n;i++) ans=(ans+(A[i]*T[i]*MM[i]%lin+lin)%lin)%lin;
	write(ans,1);
}

int main (){
	read();
}

扩展中国剩余定理

说明:中国剩余定理和扩展中国剩余定理只有名字相似罢了,思路基本不同!!

求解方程

f(x)={xa1(modm1)xa2(modm2)...xan(modmn)m1,m2,m3...,mn$

思路推导

由于不保证 m1,m2,m3..,mn 互质,所以考虑将方程两个两个合并起来依次求解(可能无解)

{xa1(modm1)xa2(modm2)x=a1+y1m1  y1Z(1)x=a2+y2m2  y2Zy2m2y1m1=a1a2gcd(m1,m2)|a1a2d=gcd(m1,m2)m2dy2m1dy1=a1a2d         m2dy2a1a2d(modm1d)m2dm1dinx[m2d]m1d           m2dm1dinx[m2d]m2dy2a1a2dinx[m2d](modm1d)y2inx[m2d]a1a2d(modm1d)(2)y2=inx[m2d]a1a2d+ym1d(1)(2)x=a2+m2inx[m2d]a1a2d+ym1m2d xa2+m2inx[m2d]a1a2d(modm1m2d) xa1+m1inx[m1d]a2a1d(modm1m2d)

注意:在写代码时,注意变量名

CODE

#include<bits/stdc++.h>
#define ll __int128
using namespace std;
const ll maxn = 423;
ll int_maxn=1e9;
ll ll_maxn=1e18;
const ll mod=1e9+7;
const ll cs=998244353;
inline ll read_int(){
    ll a=0,f=0,g=getchar();
    while(g<'0'||g>'9'){if(g=='-') f=1;g=getchar();}
    while('0'<=g&&g<='9') a=a*10+g-'0',g=getchar();
    return f ? -a : a;
}
inline void write(ll s,bool f){
    ll top=0,a[40];
    if(s<0) s=-s,putchar('-');
    while(s) a[++top]=s%10,s/=10;
    if(top==0) a[++top]=0;
    while(top) putchar(a[top]+'0'),top--;
    if(f) putchar('\n');
}

ll n;
ll a1,a2,m1,m2;
ll ans;

inline ll exgcd(ll a,ll b,ll& x,ll& y){
	if(!b){x=1,y=0;return a;}
	ll gcd=exgcd(b,a%b,y,x);
	y-=a/b*x;
	return gcd;
}

inline bool Extended_Chinese_remainder_theorem(){
	ll x,y;
	ll d=exgcd(m1,m2,x,y);
	ll c=a2-a1;
	if(c%d) return 0;
	x=((c/d*x)%(m2/d)+m2/d)%(m2/d);
	ll mod=m1/d*m2;
	a1=((m1*x+a1)%mod+mod)%mod;
	m1=mod;
	return 1;
}

inline void read(){
	n=read_int();
	a1=read_int(),m1=read_int();
	for(ll i=1;i<n;i++){
		a2=read_int(),m2=read_int();
		if(!Extended_Chinese_remainder_theorem()) {write(-1,0);return;}
	}
	write(a1%m1,1);
}

int main (){
	read();
}

总结有以下毒瘤之处:

  • 注意时刻保证a1,m1 处于正确的范围之中
  • 注意判断无解条件 a2a1%d !=0
  • 多开 __int128
  • 一定要熟练运用裴储定理以及同余性质!
posted @   轩Demonmaster  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示