问题一:给你n个方程组:
x%m[0] = a[0]
x%m[1] = a[1]
···
x%m[n-1] = a[n-1]
求变量x 的值,其中m[]必须两两互质。
直接exgcd可以KO
LL gcd(LL a, LL b){
return b == 0 ? a : gcd(b, a%b);
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
if(!b){d = a, x = 1, y = 0;}
else
{
exgcd(b, a%b, d, y, x);
y -= x * (a / b);
}
}
//l-r为方程组下标 方程 x % m = a
LL CRT(LL l, LL r, LL *m, LL *a)
{
LL M = 1, d, y, x = 0;
for(LL i = l; i <= r; i++)
M = M / gcd(M, m[i]) * m[i];
for(LL i = l; i <= r; i++)
{
LL w = M / m[i];
exgcd(m[i], w, d, d, y);
x = (x + y * w * a[i]) % M;
}
return (x + M) % M;
}
问题二:给你n个方程组:
x%m[0] = a[0]
x%m[1] = a[1]
···
x%m[n-1] = a[n-1]
求变量x 的值,无解输出-1。其中m[]的值没有要求。
这时需要用到合并方程的做法。因为exgcd只能求解一个方程a * x + b * y = c的解,我们需要把n个方程合并成一个。
合并方程的讲解如下:来自九野的博客
问题描述:给出bi,ni的值,且n1, n2, n3,…, ni两两之间不一定互质,求Res的值?
解:采用的是合并方程的做法。
这里将以合并第一第二个方程为例进行说明
由上图前2个方程得(设k1、k2为某一整数):
所以我们简化一下结论:
已知方程组(b1,b2,n1,n2是已知量):
res%b1 = n1
res%b2 = n2
->
合并两条方程得到:
res % ( (n1*n2)/d ) = b1+n1*( K%(n2/d))
其中k = (k1*(b2-b1)/d) % (n2/d);
其中d = gcd(n1,n2);
其中k1:
k1*n1 - k2*n2 = b2-b1
k1, d 可以直接由extend_gcd得到 extend_gcd(n1,n2,d,k1,k2);
(b2-b1)%d == 0 说明extend跑出的k1是一个解,否则说明不存在满足解的k1
注意求k时:为了得到最小非负整数k,所以用一个取模的技巧
k = (k%mod+mod)%mod, 其中mod = m[i] / d,每一次的mod值可能不一样;
实现:
LL gcd(LL a, LL b){
return b == 0 ? a : gcd(b, a%b);
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
if(!b){d = a, x = 1, y = 0;}
else
{
exgcd(b, a%b, d, y, x);
y -= x * (a / b);
}
}
LL CRT(LL l, LL r, LL *m, LL *a)//方程 x % m = a
{
LL lcm = 1;
for(LL i = l; i <= r; i++)
lcm = lcm / gcd(lcm, m[i]) * m[i];
for(LL i = l+1; i <= r; i++)//合并方程 共需r-l+1次
{
LL A = m[l], B = m[i], d, x, y, c = a[i] - a[l];//合并方程后 各个系数
exgcd(A, B, d, x, y);//d = gcd(A, B);
if(c % d)//对扩展后的式子a*x + b*y = c 当且仅当c % d == 0是才有解
return -1;
LL mod = m[i] / d;
LL k = ((x * c / d) % mod + mod) % mod;
a[l] = m[l] * k + a[l];
m[l] = m[l] * m[i] / d;//更新方程系数 利用该方程继续和下一个方程合并
}
if(a[l] == 0) return lcm;//特判
return a[l];//合并到最后a[l] 就是解
}