怎样求同余方程组?如:
[egin{cases}
x equiv a_1 pmod {m_1} \
x equiv a_2 pmod {m_2} \
cdots \
x equiv a_n pmod {m_n}
end{cases}]
不保证 (m) 两两互素?
两两合并!
比方说
[egin{cases}
x equiv a_1 pmod {m_1} \
x equiv a_2 pmod {m_2} \
end{cases}]
就是
[egin{cases}
x = m_1x_1+a_1\
x = m_2x_2+a_2\
end{cases}]
可以变形成
[m_1x_1+m_2(-x_2)=a_2-a_1
]
拿扩欧搞掉这个方程。我们肯定想让 (x) 最小,那就让 (x_1) 最小,这样就求出了 (x) 的特解 (x')。
显然, (x) 的通解是 (x=x'+[m_1,m_2] imes t ,t in mathbb{Z})。
这也就很像是
[x equiv x' pmod {[m_1,m_2]}
]
我们惊喜地发现两个方程变成了一个方程。一路做下去就好了。
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int n;
ll a, r, m, aa, rr, x, y;
bool flag;
ll exgcd(ll a, ll b, ll &x, ll &y){
if(!b){
x = 1;
y = 0;
return a;
}
ll re=exgcd(b, a%b, x, y);
ll z=x;
x = y;
y = z - a / b * y;
return re;
}
int main(){
while(scanf("%d", &n)!=EOF){
flag = true;
n--;
scanf("%lld %lld", &aa, &rr);
while(n--){
scanf("%lld %lld", &a, &r);
if(!flag) continue;
ll gcd=exgcd(aa, a, x, y);
if((r-rr)%gcd) flag = false;
else
x = (((r-rr)/gcd*x)%(a/gcd)+a/gcd)%(a/gcd);
x = rr + x * aa;
rr = x;
aa = a/gcd*aa;
}
if(flag) printf("%lld
", x);
else printf("-1
");
}
return 0;
}
如果保证两两互素呢?那就中国剩余定理了。记 (m =prod_{i=1}^n m_i),(M_i=m/m_i),(t_i) 是 (M_i) 在模 (m_i) 意义下的乘法逆元,则一个特解是 (sum_{i=1}^n a_iM_it_i)。通解是 (sum_{i=1}^n a_iM_it_i + mk, k in mathbb{Z})。
证明:因为当 (i
ot =j)时,(m_j|M_i),则 (a_iM_it_i equiv 0 pmod {m_j}),而 (a_jM_jt_j equiv a_j pmod {m_j}),证毕。
#include <iostream>
#include <cstdio>
using namespace std;
int a[15], cnt, mul, ans, x, y, dd;
const int m[]={0, 23, 28, 33};
int exgcd(int aa, int bb, int &x, int &y){
if(!bb){
x = 1;
y = 0;
return aa;
}
int re=exgcd(bb, aa%bb, x, y);
int z=x;
x = y;
y = z - aa / bb * y;
return re;
}
int ni(int aa, int bb){
int gcd=exgcd(aa, bb, x, y);
return (x%bb+bb)%bb;
}
int main(){
while(scanf("%d %d %d %d", &a[1], &a[2], &a[3], &dd)!=EOF){
mul = 1;
if(a[1]<0) break;
ans = 0;
for(int i=1; i<=3; i++)
a[i] %= m[i], mul *= m[i];
for(int i=1; i<=3; i++)
ans += a[i] * (mul/m[i])%mul * ni(mul/m[i], m[i])%mul;
ans -= dd;
ans = (ans%mul+mul)%mul;
if(!ans) ans += mul;
printf("Case %d: the next triple peak occurs in %d days.
", ++cnt, ans);
}
return 0;
}