前言
题目链接
前置知识
- 乘法逆元
- 扩展欧几里得
- 一些简单的数学知识(小学奥数)
正文
引子
在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。
在《孙子歌诀》中给出了解决这个问题的解法:三人同行七十稀,五树梅花廿一支,七子团圆正半月,除百零五便得知。很是朗朗上口,但这是什么意思呢?
具体解法分三步:
找出三个数:
1.从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
2.用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加(152+213+70*2)得到和233。
3.用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。
说实话我不知道这是怎么想出来的,tql。
现在原问题可以化成:
[ f(x)=left{
egin{aligned}
x≡a_1 pmod{m_1} \
x≡a_2 pmod{m_2}\
x≡a_3 pmod{m_3}\
x≡a_4 pmod{m_4}\
end{aligned}
ight.
]
现在假设$m_1,m_2,m_3,m_4,...,m_n相互互质,则可以进行如下构造
令:$$M=prod_{i=1}^nm_i$$设:$$M_i=M/m_i,t_i=exgcd(M_i,m_i).x$$
则x的通解为:
[x=KM+sum_{i=1}^na_i*t_i*M_i
]
所以最小的x为:
[x=sum_{i=1}^na_i*t_i*M_i
]
然后直接算就好了
code
#include<bits/stdc++.h>
#define rg register
#define int long long
#define file(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+c-48,c=getchar();
return f*x;
}
void exgcd(int a,int b ,int &x,int &y){
if(!b){x=1,y=0;return;}
exgcd(b,a%b,x,y);
int t=x;
x=y,y=t-(a/b)*y;
}
int n,x,y,a[11],p[11];
void crt(){
int ans=1,js=0;
for(int i=1;i<=n;i++)
ans*=p[i];
for(int i=1;i<=n;i++){
int t=ans/p[i];
exgcd(t,p[i],x,y);
js+=a[i]*t%ans*x%ans;
js%=ans;
}
printf("%lld",(js+ans)%ans);
}
void init(){
n=read();
for(int i=1;i<=n;i++)
p[i]=read(),a[i]=read();
}
main(){
// file("");
init();
crt();
return 0;
}