中国剩余定理学习笔记

中国剩余定理学习笔记。

前置技能:扩展欧几里得算法。

中国剩余定理

对于这样一个模方程组:

\[\begin{cases} x=r_1\%m_1 \\ x=r_2\%m_2 \\ ...... \\ x=r_n\%m_n \end{cases} \]

其中\(m_1,m_2,...m_n\)两两互质。

\(x\)的最小正整数解。

定理:

\(M=\prod_{i=1} ^{n}m_i\),因为其两两互质,所以\(M\)为他们的最小公倍数。

\(t_i\)为方程$ \frac M{m_i}*t_i=1(mod,m_i) $的解

很显然可以利用扩展欧几里得解出\(t_i\)

对于当前的模方程,我们只用和上一组通解合并即可。

通解为\(x_i=x_0+i*M\);

代码

#include<bits/stdc++.h>
using namespace std;

int a[15],b[15];

void exgcd(int a,int b,int &d,int &x,int &y) {
	if(b)exgcd(b,a%b,d,y,x),y-=(a/b)*x;
	else d=a,y=0,x=1;
}

int main() {
	int n,ans=0,sum=1,d,y;
	scanf("%d",&n);
	for(int i=0; i<n; i++)
		scanf("%d%d",&a[i],&b[i]);
	for(int i=0; i<n; i++) sum*=a[i];
	for(int i=0; i<n; i++) {
		int w=sum/a[i];
		exgcd(a[i],w,d,d,y);
		ans=(ans+y*w*b[i])%sum;
	}
	ans=(ans+sum)%sum;
	printf("%d",ans);
}

扩展中国剩余定理

同上,只是\(m_i\)不一定互质。

推导

\(m\)个方程组的解集为\(Ans+M*K\),现在合并下一个方程。

当前方程为\(x=r_i(mod,m_i)\)

则问题装换为,找到一个最小的\(t\)

满足\(Ans+t*M+m_i*y=r_i\),

\(t*M+m_i*y=r_i-Ans\);

由于\(M\)\(m_i\)都是已知量,我们利用扩展欧几里得即可,

解出\(t\),带回合并即可。

代码

#include <bits/stdc++.h>
 
using namespace std;
 
#define int long long
#define reg register
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
 
inline int Read() {
    int res=0,f=1;
    char c;
    while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),c>=48&&c<=57);
    return f?res:-res;
}
 
template<class T>inline bool Min(T &a,T const&b) {
    return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a,T const&b) {
    return a<b?a=b,1:0;
}
 
const int N=1e5+5;
const int dx[4]= {1,-1,0,0},dy[4]= {0,0,1,-1};

int n,mod[N],res[N];

int Exgcd(int a, int b, int &x, int &y) {
	if(!b) {
		x=1,y=0;
		return a;
	}
	int g=Exgcd(b,a%b,y,x);
	y-=(a/b)*x;
	return g;
}

inline int Excrt() {
	int M=mod[1],ans=res[1],x,y;
	rep(i,2,n){
		int g=Exgcd(M,mod[i],x,y);
		if((res[i]-ans)%g) return -1;
		x*=(res[i]-ans)/g,y=mod[i]/g, x=(x%y+y)%y;
		ans=M*x+ans, M=M/g*mod[i], ans%=M;
	}
	int z=(ans%M+M)%M;
	if(!z)z=M;
	return z;
}

signed main() {
	scanf("%lld",&n);
	rep(i,1,n)mod[i]=Read(),res[i]=Read();
	int Ans=Excrt();
	printf("%lld\n",Ans);
	return 0;
}
posted @ 2019-09-21 13:59  dsjkafdsaf  阅读(159)  评论(0编辑  收藏  举报