详细介绍+理解
写的非常好,一看就懂。
《孙子算经》问题简化:
- 已知 n%3=2, n%5=3, n%7=2, 求n。
“中国剩余定理”具体解法分三步:
注意定理中三个数两两互质
- 从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
即满足:
lcm(3,5)%7=1 的最小数 15
lcm(3,7)%5=1 的最小数 21
lcm(5,7)%3=1 的最小数 70 - 用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加。
即:
(15x2+21x3+70x2)=233 - 用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。
即:
n =233%lcm(3,5,7) = 23 这是n的最小解
中国剩余模版(互质)
ll extend_gcd(ll a, ll b, ll &x, ll &y){
ll res=a;
if (b!=0){
res=extend_gcd(b,a%b,y,x);
y-=(a/b)*x;
}
else {
x=1;
y=0;
}
return res;
}
// / m[i] 余 a[i]
ll china(int len, ll *m, ll *a){
ll M=1,w,d,x,y,ret=0;
for (int i=0; i<len; i++) M*=m[i];
for (int i=0; i<len; i++){
w=M/m[i];
d=extend_gcd(m[i],w,x,y);
ret=(ret+y*w*a[i])%M;
}
return (M+ret%M)%M;
}
扩展中国剩余模版(不互质)
ll m[maxn],a[maxn];
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll extend_gcd(ll a, ll b, ll &x, ll &y) {
ll res=a;
if (b!=0) {
res=extend_gcd(b,a%b,y,x);
y-=(a/b)*x;
} else {
x=1;
y=0;
}
return res;
}
ll exchina(ll n) {
ll m1=m[0],a1=a[0];
ll m2,a2,k1,k2,x0,g,c;
ll lcm=m[0];
for(int i=1; i<n; i++) {
m2=m[i];
a2=a[i];
c=a2-a1;
g=extend_gcd(m1,m2,k1,k2);
lcm=lcm*m[i]/gcd(lcm,m[i]);
if(c%g) return -1; //c如果不是g的倍数,则无解
x0=k1*c/g;
ll t=m2/g;
x0=(x0%t+t)%t; //保证x0为最小正数 c/t是余数,系数扩大余数倍
a1+=m1*x0;
m1=t*m1;
}
if (a1==0){ //余数为0,说明m[]是等比数列。且余数都为0
a1=1;
for (int i=0; i<n; i++)
a1=a1*m[i]/gcd(a1,m[i]);
}
return a1;
}
应用
POJ1006 AC代码
中国剩余定理
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
ll a[4];
ll m[4]={23,28,33};
const int mod=21252;
ll extend_gcd(ll a, ll b, ll &x, ll &y) {
ll res=a;
if (b!=0) {
res=extend_gcd(b,a%b,y,x);
y-=(a/b)*x;
} else {
x=1;
y=0;
}
return res;
}
ll china(int len, ll *m, ll *a) {
ll M=1,w,d,x,y,ret=0;
for (int i=0; i<len; i++) M*=m[i];
for (int i=0; i<len; i++) {
w=M/m[i];
d=extend_gcd(m[i],w,x,y);
ret=(ret+y*w*a[i])%M;
}
return (M+ret%M)%M;
}
int main() {
//ios::sync_with_stdio(0);
ll d;
int cnt=1;
while (cin>>a[0]>>a[1]>>a[2]>>d) {
if(a[0]==-1 && a[1]==-1 && a[2]==-1 && d==-1)
break;
ll ans=(china(3,m,a)-d)%mod;
if (ans<=0) ans+=mod;
printf("Case %d: the next triple peak occurs in %lld days.\n",cnt++,ans);
}
return 0;
}
POJ2891 AC代码
扩展中国剩余定理
#include <iostream>
#include <cstdio>
typedef long long ll;
using namespace std;
ll m[60000],a[60000];
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
ll extend_gcd(ll a, ll b, ll &x, ll &y) {
ll res=a;
if (b!=0) {
res=extend_gcd(b,a%b,y,x);
y-=(a/b)*x;
} else {
x=1;
y=0;
}
return res;
}
ll exchina(ll n) {
ll m1=m[0],a1=a[0];
ll m2,a2,k1,k2,x0,g,c;
ll lcm=m[0];
for(int i=1; i<n; i++) {
m2=m[i];
a2=a[i];
c=a2-a1;
g=extend_gcd(m1,m2,k1,k2);
lcm=lcm*m[i]/gcd(lcm,m[i]);
if(c%g) return -1;
x0=k1*c/g;
ll t=m2/g;
x0=(x0%t+t)%t;
a1+=m1*x0;
m1=t*m1;
}
if (a1==0){
a1=1;
for (int i=0; i<n; i++)
a1=a1*m[i]/gcd(a1,m[i]);
}
return a1;
}
int main() {
ios::sync_with_stdio(0);
ll n;
while (cin>>n) {
for (int i=0; i<n; i++) {
cin>>m[i]>>a[i];
}
if (n==1) {
cout<<a[0]<<endl;
continue;
}
ll ans=exchina(n);
cout<<ans<<endl;
}
return 0;
}