各位好啊,这里是蒟蒻gigo_64的第一篇博客,,这里我们开始啦。
本文需要读者知晓扩展欧几里得,如果不会请点击这个大佬的链接;https://blog.csdn.net/sslz_fsy/article/details/81566257
中国剩余定理是用来求解一个方程组的。这个方程组如下:
怎么办呢,,我想求一个x怎么办呢,,
好我们引入中国剩余定理。
1.中国剩余定理
首先我们给出一个条件:所有mi互质。好像看起来没有什么用。
现在,我们定义一些量:
ai:如题。
mi:如题。
M:所有mi的乘积。
Mi:M/mi。(也就是除了mi之外其它m的乘积)
Mi^-1:Mi模mi时的逆元。
所谓中国剩余定理,就是指
接下来我们来证明为什么如此。
首先因为Mi^-1是Mi的逆元,所以
因为Mi是包含mj的(j!=i),所以明显
既然如此,就有
后面那个t*M是一个通解,因为M是所有m的乘积所以肯定满足
那么我们得到了中国剩余定理:(所有m互质)
其实我也不太明白为什么x从通解那个地方能转换到这里的定理。
上代码吧。
但我现在可以引入中国剩余定理·扩展
2.扩展中国剩余定理。
定义很简单,中国剩余定理的基础上加上了!!!m不一定互质!!!
这下会很麻烦,,因为m不一定互质会让之前推出剩余定理的过程一开始就不成立。
对此,我们可以使用这样一个技能。
假设你现在得到了满足前k个方程的通解x。
正如我刚刚所说,x的通解是加上了t*M的,可惜这个M是前k个m的最小公倍数,而不是所有m的。
我们设前k个方程的通解为ans,之前的M为lcm,下一个方程为x=a[k+1](mod m[k+1]);
那我们现在的目标是让x满足前面方程的前提下满足x=a[k+1](mod m[k+1]);
也就是说,要满足ans+t*lcm=a[k+1]+m[k+1]*y;
恒等变形得到t*lcm-y*m[k+1]=a[k+1]-ans;并且我把减号改成加好不会有问题因为y是没什么用的。
所以t*lcm+y*m[k+1]=a[k+1]-ans好像很眼熟,
这是个扩展欧几里得的模板样子的等式,lcm,m[k+1],a[k+1],ans都是已知。
直接上扩欧的模板exgcd(lcm,m[k+1])就可以得到t,并且在得到t的过程中顺带得到lcm与m[k+1]的最大公约数gcd
要注意一个事实,就是我们求到的t是满足左边=gcd而非a[k+1]-ans的。
那直接让t=t/gcd*a[k+1]-ans。因为t是整数,所以如果(a[k+1]-ans)%gcd!=0的话,这个方程组无解。
现在我们得到了真正的t,那新的ans=ans+t*lcm;
一定要在更新完ans后再更新lcm。lcm=lcm*m[i]/gcd;(两个数的最小公倍数就是两数相乘除以最大公约数嘛)
然后记得ans要取模不然极可能爆long long。ans=(ans%lcm+lcm)%lcm;
上代码,带注释,这里是洛谷4777,因为会爆longlong所以用快速幂分解处理了t*lcm。
#include<bits/stdc++.h>
using namespace std;
long long n;
long long a[100003],m[100003],M=1,ans,x,y,gcd;
void exgcd(long long a,long long b,long long &x,long long &y){
if(!b){
x=1,y=0;gcd=a;return;//就是扩欧模板,不知道的可以去sslz_fsy那里看模板ovo
}
exgcd(b,a%b,y,x);y-=a/b*x;
}long long t;
void mul(long long a,long long b,long long p){
long long ans=0;
while(b){//将两个数相乘分解成很多个数*b的和,边加边%防止爆longlong
if(b&1)ans+=a,ans%=p;
a=a*2%p;
b>>=1;
}//可以自己理解一下
t=ans;//看,这个才是真正的t
}
int main(){
cin>>n;
for(register int i=1;i<=n;i++)scanf("%lld %lld",&m[i],&a[i]);
long long lcm=m[1],ans=a[1];//第一个方程直接上
for(register int i=2;i<=n;i++){
long long c=(a[i]-ans%m[i]+m[i])%m[i];//防止爆炸的模加模,c就是a[k+1]-ans
exgcd(lcm,m[i],x,y);//求出的x是初步的假t ,顺带求出gcd
mul(x,c/gcd,m[i]/gcd);//意思是x*(c/gcd)%m[i]/gcd
ans=t*lcm;//经过mul得到的t就是真正的t啦,直接加
lcm=lcm*m[i]/gcd;//更新lcm
ans=(ans%lcm+lcm)%lcm;//防止爆炸的模加模
}
cout<<ans;//万恶的cout
return 0;
}
我的第一篇博客非常非常非常啰嗦。希望以后自己能看得懂哈哈哈
请多指教啦ovo