超链接:数学合集
问:今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
换成人话(这才不是人话好吗):
解方程:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) x ≡ a 3 ( m o d m 3 ) ⋮ x ≡ a n ( m o d m n ) \left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ x & \equiv a_3(mod\ m_3)\\ \vdots\\ x & \equiv a_n(mod\ m_n)\\ \end{aligned} \right. ⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧xxx⋮x≡a1(mod m1)≡a2(mod m2)≡a3(mod m3)≡an(mod mn) ( m 1 , m 2 , m 3 ⋯ ⋯ m n 互 质 , I t ′ v e r y i m p o r t a n t ) (m_1,m_2,m_3 \cdots \cdots m_n互质,It'\ very \ important) (m1,m2,m3⋯⋯mn互质,It′ very important)
我们先来讲肿么做,再来讲为啥可以这样做
其实 M i M_i Mi就是除了 m i m_i mi以外的其他所有 m i m_i mi的lcm(←别忘了它们是两两互质的)
也就是说M_i扩大了任意被模其他 m i m_i mi都是不受影响的
那我们就可以随便扩大,让 M i m o d m i = a i M_i\ mod \ m_i=a_i Mi mod mi=ai不就好了吗?
至于如何让 M i M_i Mi变成我们想要的值,其实我们先要算的是 M i M_i Mi在模 m i m_i mi下的逆元(逆元,什么?戳),也就是我们想让 k ∗ M i m o d m i = 1 k*M_i\ mod\ m_i=1 k∗Mi mod mi=1,这是再把 k ∗ M i k*M_i k∗Mi扩大相应的倍数,就行了
OK,完事
int china()
{
int ans=0,lcm=1,x,y;
for(int i=1;i<=n;i++) lcm*=m[i];
for(int i=1;i<=n;i++)
{
int M=lcm/m[i];
ex_gcd(M,m[i],x,y);
x=(x%m[i]+m[i])%m[i];
ans=(ans+M*x*a[i])%lcm;
}
return (ans+lcm)%lcm;
}
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) x ≡ a 3 ( m o d m 3 ) ⋮ x ≡ a n ( m o d m n ) \left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ x & \equiv a_3(mod\ m_3)\\ \vdots\\ x & \equiv a_n(mod\ m_n)\\ \end{aligned} \right. ⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧xxx⋮x≡a1(mod m1)≡a2(mod m2)≡a3(mod m3)≡an(mod mn)
一样的方程只不过这里的 m i m_i mi不互质了
它与中国剩余定理半毛钱关系也木有~~
我们先来看一看如果只有两个方程的情况:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) \left\{ \begin{aligned} x & \equiv a_1(mod\ m_1)\\ x & \equiv a_2(mod\ m_2)\\ \end{aligned} \right. {xx≡a1(mod m1)≡a2(mod m2)
这个方程组其实可以转化成:
{ x = a 1 + k 1 ∗ m 1 x = a 2 + k 2 ∗ m 2 \left\{ \begin{aligned} x=a_1+k_1*m_1\\ x=a_2+k_2*m_2\\ \end{aligned} \right. {x=a1+k1∗m1x=a2+k2∗m2
然后我们把两式相减,就得到了:
k 1 ∗ m 1 − k 2 ∗ m 2 = a 2 − a 1 k_1*m_1-k_2*m_2=a_2-a_1 k1∗m1−k2∗m2=a2−a1
哦,有没有发现这是一个形如这个的方程: a ∗ x + b ∗ y = c a*x+b*y=c a∗x+b∗y=c
也就是说我们可以用扩展欧几里得解决(那是什么?戳)
于是,我们就得到了 k 1 k_1 k1和 − k 2 -k_2 −k2
然后 x 0 = k 1 ∗ m 1 + a 1 x_0=k_1*m_1+a_1 x0=k1∗m1+a1
我们也就知道了方程的通解是: x = x 0 + m ∗ l c m ( m 1 , m 2 ) x=x_0+m*lcm(m_1,m_2) x=x0+m∗lcm(m1,m2)(不停扩大或减小 l c m ( m 1 , m 2 ) lcm(m_1,m_2) lcm(m1,m2)对它们的余数是没有影响的)
也就是说: x ≡ x 0 ( m o d l c m ( m 1 , m 2 ) ) x \equiv x_0(mod\ lcm(m_1,m_2)) x≡x0(mod lcm(m1,m2))
有没有发现我们把两个方程合并成了 一个!!
然后不停地合并下去,我们就得到了答案!
poj2891:
#include
#include
#define ll long long
using namespace std;
int n;
ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
void ex_gcd(ll a,ll b,ll &x,ll &y){
if(!b) x=1,y=0;
else ex_gcd(b,a%b,y,x),y-=(a/b)*x;
}
ll mul(ll a,ll b,ll p){
ll ans=0;
while(b){
if (b%2) ans=(ans+a)%p;
a=(a+a)%p;
b/=2;
}
return ans;
}
int main(){
ll m2,a2,m,ans;
while(~scanf("%d",&n)){
bool flag=0;
scanf("%lld%lld",&m,&ans);
for (int i=2;i<=n;i++){
scanf("%lld%lld",&m2,&a2);
a2=(a2-ans%m2+m2)%m2;//a2-a1
ll d=gcd(m2,m),x,y;
if (a2%d) flag=1;//方程无解
ex_gcd(m,m2,x,y);
x=mul(x,a2/d,m2);//解k1*m1-k2*m2=a2-a1 (如果你想过掉【洛谷P4777】这里要加一个龟速乘)
ans+=x*m;
m*=m2/d;//m1=lcm(m1,m2)
ans=(ans%m+m)%m;
}
if (flag) printf("-1\n");
else printf("%lld\n",ans);
}
return 0;
}
OK,完事
参考:
https://www.cnblogs.com/freinds/p/6388992.html
https://blog.csdn.net/niiick/article/details/80229217
https://www.cnblogs.com/Miracevin/p/9254795.html
https://www.luogu.org/recordnew/show/19877167
https://www.luogu.org/recordnew/show/19921331
于HG机房