中國剩餘定理——楊子曰數學
超鏈接:數學合集
問:今有物不知其數,三三數之剩二,五五數之剩三,七七數之剩二。問物幾何?
換成人話(這纔不是人話好嗎):
解方程:
中國剩餘定理
我們先來講腫麼做,再來講爲啥可以這樣做
How to do?
- 求出的lcm
比如文章最上面的那道題,我們先求出 - 讓
於是,我們有了: - 讓M_i擴大一定的倍數s,使得
在這個例子中:
歐,發現不用擴大就已經滿足了
繼續,
餘數需要變成3,那我們就把被除數變成原來的3倍
也就是
然後,
餘數得是2,那我們就要把變爲2倍
也就是: - 把擴大後的全部加起來,我們就得到了答案:
- 如果你喜歡的話把,使答案落在0~m之間
OK,完事
Why這樣做我們可以找到答案?
其實就是除了以外的其他所有的lcm(←別忘了它們是兩兩互質的)
也就是說M_i擴大了任意被模其他都是不受影響的
那我們就可以隨便擴大,讓不就好了嗎?
至於如何讓變成我們想要的值,其實我們先要算的是在模下的逆元(逆元,什麼?戳),也就是我們想讓,這是再把擴大相應的倍數,就行了
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;
}
擴展中國剩餘定理
一樣的方程只不過這裏的不互質了
它與中國剩餘定理半毛錢關係也木有~~
我們先來看一看如果只有兩個方程的情況:
這個方程組其實可以轉化成:
然後我們把兩式相減,就得到了:
哦,有沒有發現這是一個形如這個的方程:
也就是說我們可以用擴展歐幾里得解決(那是什麼?戳)
於是,我們就得到了和
然後
我們也就知道了方程的通解是:(不停擴大或減小對它們的餘數是沒有影響的)
也就是說:
有沒有發現我們把兩個方程合併成了 一個!!
然後不停地合併下去,我們就得到了答案!
poj2891:
#include<iostream>
#include<cstdio>
#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機房