原理及例题讨论
非质数取模参考
扩展中国剩余定理,使用非互质情况
如何利用中国剩余定理求解非质数取模即注意事项,以及为什么可以这样做?
举例,比如模37035
mo = 37035
假设一个很大的数A=105641,要计算Amod(mo)=B
计算B的方法为:
首先通过质数分解后 37035=95823,然后分别计算A对于9,5,823的取模结果,然后利用中国剩余定理计算最小的能够满足模9,5,823的结果与已知结果相同的最小的数。
证明:
隐含条件:A对9,5,823的取模结果与B对9,5,823的取模结果相同。
假设我们按照中国剩余定理求出的所有数的集合C中的最小值为
C
0
C_0
C0,那么C的集合是什么呢?
由于9,5,823之前两两互质,为了满足
C
i
C_i
Ci的取模结果不变,那么必然要加一个9,5,823的最小公倍数,也就是A。
所以C的集合就是以
C
0
C_0
C0为首项,公差为A的等差数列。
显然B就在这个数列内,且B小于A,那么B必然是这个数列中的首项,也就是最小值。
注意事项:
(1)我们这样转化就是为了利用质数的逆元方法,通过计算因数对A的取模结果,从而计算出B,但是如果因数本身不是质数,那么就要小心了。
(2)如果是质数时,需要考虑也并不是一定就可以用逆元,因为逆元的前提是除法的分母与mo互质,假如分母可以整除mo的话,即使mo是质数也不能直接用,需要对其进行特殊处理。(例题解法中就对3,5,823进行了特殊处理)
板子
互质板子
这里的u[]数组为模数数组
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){
x = 1, y = 0;
return;
}
exgcd(b, a % b, x, y);
ll t = x;
x = y, y = t - (a / b) * y;
}
ll gunc(vector<ll>&res){
ll ans = 0;
ll out = 1;
for(int i = 0;i<3;i++){
out = out*u[i];
}
for(int i = 0;i<3;i++){
ll s = out/u[i];
ll x = 0,y = 0;
exgcd(s,u[i],x,y);
if(x<0)
x += u[i];
ans += s*x*res[i];
}
ans = ans%out;
return ans;
}
非互质&互质共用板子
ll exgcd(ll a, ll b, ll &x, ll &y){
if(b==0){
x = 1, y = 0;
return a;
}
ll d, x1, y1;
d = exgcd(b, a%b, x1, y1);
x = y1, y = x1 - a/b*y1;
return d;
}
ll excrt(vector<ll>& m, vector<ll>& r){
int n = m.size();
ll m1, m2, r1, r2, p, q;
m1 = m[0], r1 = r[0];
for(int i = 1; i < n; ++i){
m2 = m[i], r2 = r[i];
ll d = exgcd(m1, m2, p, q);
if( (r2-r1)%d !=0 ) return -1; // 不能整除,说明无解
p = p*(r2-r1)/d;
p = (p%(m2/d) + (m2/d))%(m2/d);
r1 = m1*p+r1;
m1 = m1*m2/d;
}
return (r1%m1+m1)%m1;
}