中国剩余定理总结
当你遇到x == c (mod p) 要你求解x的时候,是不是很容易的想到了这样转换---> x - py = c运用extgcd得到答案。但是现在如果有很多个x呢?如:
x == c1(mod p1)
x == c2(mod p2)
...............................
x == cn(mod pn)
这时候你要如何求解呢?肯定不能像上面一样吧!
其实这类问题有一个专门的算法成为孙子定理流行叫法是中国剩余定理(China Remainder Theorem)。
一般一个算法的出现肯定有其的约束条件吧?对啊。这个普通版的CRT的限制是模之间是两两互素的。但是不互难道就不能求解了吗?当然也可以,这时候就要用CRT的扩展版。下面分别举例来说明和训练。
一、当模两两互质的条件下
题目链接:Click Here~
分解出题目的意思:现在假设总共有X 头猪,每个猪圈Y头猪。
那么显然可以得到 ---> X - Y*ai = bi 根据同余式的定义对式子转换---> X == bi(mod ai)。结果很裸了。套模板的时候。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long LL;
const int MAXN = 10 + 10;
int n,a[MAXN],m[MAXN];
void extgcd(LL a,LL b,LL& d,LL& x,LL& y){
if(!b) { d = a; x = 1; y = 0; }
else { extgcd(b,a%b,d,y,x); y -= x*(a/b); }
}
LL china(int n,int *a,int *m){
LL M = 1,d,y,x = 0;
for(int i = 0;i < n;++i) M *= m[i];
for(int i = 0;i < n;++i){
LL w = M / m[i];
extgcd((LL)m[i],w,d,d,y);
x = (x + y*w*a[i]) % M;
}
return (x+M)%M;
}
int main()
{
while(~scanf("%d",&n)){
for(int i = 0;i < n;++i)
scanf("%d%d",&m[i],&a[i]);
printf("%I64d\n",china(n,a,m));
}
return 0;
}
二、当模两两不互质
题目链接:Click Here~
题目很简单就不解释了。最要来看看如何转换成CRT来求解。现在我们假设每堆硬币都有Y个,则可以得到以下等式:1*X - Y*Mi = Ai ---> X == Ai(mod Mi) 下面就是模板问题了。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
int n,times;
LL A[MAXN],r[MAXN]; //x == Ai(mod ri)
LL gcd(LL a,LL b){
return b?gcd(b,a%b):a;
}
void ex_gcd(LL a,LL b,LL& d,LL& x,LL& y){
if(!b) { d = a; x = 1; y = 0; }
else { ex_gcd(b,a%b,d,y,x); y -= x*(a/b); }
}
void read_case(){
scanf("%d",&n);
lcm = 1;
for(int i = 1;i <= n;++i){
scanf("%lld",&A[i]);
lcm = lcm / gcd(lcm,A[i]) * A[i];
}
for(int i = 1;i <= n;++i) scanf("%lld",&r[i]);
}
void solve(){
read_case();
LL lcm;
LL a,b,c,d,x,y;
for(int i = 2;i <= n;++i){
a = A[1],b = A[i],c = r[i] - r[1];
ex_gcd(a,b,d,x,y);
if(c % d){printf("-1\n"); return;}
LL b1 = b / d;
x *= c / d;
x = (x % b1 + b1) % b1;
r[1] = A[1] * x + r[1];
A[1] = A[1]*(A[i] / d);
}
if(r[1] == 0) printf("%lld\n",lcm);
else printf("%lld\n",r[1]);
}
int main(){
int T;
times = 0;
scanf("%d",&T);
while(T--){
printf("Case %d: ",++times);
solve();
}
return 0;
}