chinesert
https://blog.csdn.net/wu_tongtong/article/details/79669723
(很久)之前就已经接触过CRT
我们先看简单的CRT
x≡a1 (mod m1)
x≡a2 (mod m2)
x≡a3 (mod m3)
...
x≡ak (mod mk)
其中m1,m2,...,mk两两互质,求在mod ∏mi意义下的x
这种CRT可以说是最基础的了
设M=∏ki=1mi
对于每一个方程,Mi=Mmi
设ti=M−1i(逆元)
tiMi=1 (mod mi)
x的通式形式为:
x=a1t1M1+a2t2M2+...aktkMk+kM=kM+∑ki=1aitiMi
在模M的意义下,方程组只有一个解:
ll x,y;
void exgcd(ll a,ll b) {
if (!b) {
x=1; y=0;
return;
}
exgcd(b,a%b);
ll t=x;
x=y;
y=t-(a/b)*y;
}
ll CRT() {
ll M=1,ans=0;
for (int i=1;i<=n;i++) M*=m[i];
for (int i=1;i<=n;i++) {
ll Mi=M/m[i];
exgcd(Mi,m[i]);
ans+=x*Mi*a[i]; ans%=M;
}
return ans;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
注意这里求逆元的方式是exgcd
不过要使用ta的前提条件是gcd(a,b)=1
(exgcd求解的方程等式右边一定是gcd(a,b)∗k
)
进阶版
如果不满足m两两互质,我们采用合并同余方程的方法将方程两两合并
x=a1 (mod m1) (0)
x=a2 (mod m2)
两个方程可以写成
x=m1∗t1+a1 (1)
x=m2∗t2+a2
m1t1+a1=m2t2+a2
m1t1=(a2−a1)+m2t2
m1t1=(a2−a1) (mod m2)
显然,想要有解,必有gcd(m1,m2)|(a2−a1)
设gcd(m1,m2)=d,c=a2−a1
,则有:
m1dt1=cd (mod m2d)
t1=cd(m1d)−1 (mod m2d)
令 K=cd(m1d)−1,则t1=ym2d+K,将其带入(1)得
x=m1(ym2d+K)+a1
=m1m2dy+m1K+a1
即:
x=m1K+a1 (mod m1m2d)
x=a (mod n) (2)
式(2)
中
a=m1K+a1,n=m1m2d
这样,成功的将(0)
式的两个方程合并为式(2)
最终,合并k个方程的最小x值为a%n
ll x,y,d;
void exgcd(ll a,ll b) {
if (!b) {
d=a;x=1;y=0; //d gcd
return;
}
exgcd(b,a%b);
ll t=x;
x=y;
y=t-(a/b)*y;
}
ll CRT() {
while (scnaf("%d",&n)!=EOF) {
ll a1,a2,m1,m2;
bool ff=1;
for (int i=1;i<=n;i++) {
if (i==1) {
scanf("%lld%lld",&a1,&m1); //x=a (mod m)
continue;
}
scanf("%lld%lld",&a2,&m2);
exgcd(m1,m2);
if ((a2-a1)%d) ff=0; //gcd(m1,m2)|(a2-a1)
x=x*((a2-a1)/d);
ll p=m2/d; //1式
x=(x%p+p)%p;
a1=a1+m1*x; //新余数
m1=m1/d*m2; //新模数 m1m2/d
a1=(a1%m1+m1)%m1; //2式 两次取模保证是最小整数解
}
if (ff) printf("%lld\n",a1);
else printf("-1\n");
}
}