CRT(中国剩余定理)(孙子定理)
一般的CRT只能用于解决模数彼此之间互质的情况。(以下假设 b b b表示模数, a a a表示 x x x在 m o d b mod \ b mod b意义下的值)
大概想法是构造 M = ∏ i = 1 k b i M=\prod_{i=1}^kb_i M=∏i=1kbi,对于第 i i i个方程,构造一组特殊的解 t i ti ti满足 M b i ∗ t i = 1 ( m o d b i ) \frac{M}{b_i}*t_i=1(mod\ b_i) biM∗ti=1(mod bi)。最后答案就是 ∑ i = 1 k M b i ∗ t i ∗ a i \sum_{i=1}^k\frac{M}{b_i}*t_i*a_i ∑i=1kbiM∗ti∗ai。
正确性证明就考虑对于每个方程,不对应这个方程的项 m o d b i mod\ b_i mod bi为0,恰好对应这个方程的项为 a i a_i ai。恰好构造出一组解。
ex_gcd寻找通解
对于
a
x
+
b
y
=
c
(
m
o
d
p
)
ax+by=c(mod\ p)
ax+by=c(mod p),最后
b
=
0
b=0
b=0的时候:
x
=
c
/
a
,
y
=
0
;
x=c/a,y=0;
x=c/a,y=0;
对于
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax+by=gcd(a,b)
ax+by=gcd(a,b),最后
b
=
0
b=0
b=0的时候:
x
=
1
,
y
=
0.
x=1,y=0.
x=1,y=0.
直接扩欧求出一组特值以后。假设 x x x的变化量为 δ \delta δ,那么 b ∣ a δ b|a\delta b∣aδ。如果 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1的话, b ∣ δ b|\delta b∣δ,直接 ( ( m o d b ) + b ) m o d b ((mod\ b)+b)\ mod\ b ((mod b)+b) mod b就好了。否则 a , b a,b a,b同时除以 g c d ( a , b ) gcd(a,b) gcd(a,b)。答案 x x x的变化一定是 x − T ∗ b ′ x-T*b' x−T∗b′的形式了。
l u o g u P 3868 luoguP3868 luoguP3868
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double LDB;
LL a[15], b[15], M = 1;
LL ans;
int n;
inline void ex_gcd(LL a, LL b, LL c, LL &x, LL &y) {
if (!b) {
x = c / a;
y = 0;
return ;
}
ex_gcd(b, a % b, c, y, x);
y -= (a / b) * x;
}
inline LL q_c(LL x, LL y, LL P) {
return (x * y - (LL)((LDB)x * y / P) * P);
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%lld", &b[i]), M *= b[i];
for (int i = 1; i <= n; ++i) {
LL x, y;
ex_gcd(M / b[i], b[i], 1, x, y);
x = ((x % b[i]) + b[i]) % b[i];
ans = (ans + q_c(q_c(M / b[i], x, M), a[i], M)) % M;
}
printf("%lld\n", ans);
return 0;
}
扩展CRT和CRT没有什么关系。
扩展CRT是假设前
k
−
1
k-1
k−1个方程的答案已经得出,通解是
x
=
x
0
+
t
∗
M
x=x_0+t*M
x=x0+t∗M的形式。
M
M
M是前
k
−
1
k-1
k−1个
b
b
b的
l
c
m
lcm
lcm。
现在要求一个
x
x
x使得满足第
k
k
k个方程,也就是找到一个
t
0
t_0
t0使得
x
0
+
t
0
∗
M
=
a
i
(
m
o
d
b
i
)
x_0+t_0*M = a_i(mod\ b_i)
x0+t0∗M=ai(mod bi)。 这个
t
0
t_0
t0可以用扩展欧几里得求出来,然后就求出了满足前
k
k
k个方程的一组特解
x
=
x
0
+
t
0
∗
M
x=x_0+t_0*M
x=x0+t0∗M。此时再把
M
M
M更新就可以了。
具体实现的时候,当前计算的 x x x是不可以取模的,往往都会要求答案不超过 l o n g l o n g long\ long long long的范围。但是中间计算可能会爆 l o n g l o n g long\ long long long。这个时候里面要写 O ( 1 ) O(1) O(1)快速乘。
另,此题最好应采用 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)形式的扩欧。对于 a x + b y = c ax+by=c ax+by=c的不定方程的解就是 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的解乘以 c g c d ( a , b ) \frac{c}{gcd(a,b)} gcd(a,b)c,如果要调整的话, x + = b g c d ( a , b ) , y − = a g c d ( a , b ) 。 x+=\frac{b}{gcd(a,b)},\ y-=\frac{a}{gcd(a,b)}。 x+=gcd(a,b)b, y−=gcd(a,b)a。而且一开始扩欧的系数都要是正的。两种CRT都要把答案对M取模!
一个巧合: a x = 1 ( m o d b ) ax=1(mod\ b) ax=1(mod b)的同余方程,即 a x + b y = 1 ( m o d b ) ( g c d ( a , b ) = 1 ) ax+by=1(mod\ b)(gcd(a,b)=1) ax+by=1(mod b)(gcd(a,b)=1)。如果扩欧解出了这个的一组解 ( x 0 , y 0 ) (x0,y0) (x0,y0)的话。 x x x的调整系数恰好是 b / 1 = b b/1=b b/1=b。造成可以直接模 b b b的效果。实际上,对于一个任意的不定方程,调整应该如上调整。
板子: l u o g u P 4777 luoguP4777 luoguP4777
l u o g u P 2480 luoguP2480 luoguP2480 猪文
题意非常清楚,就是求 G ∑ d ∣ n C n n d ( m o d 999911659 ) G^{\sum_{d|n}C_n^{\frac{n}{d}}} (mod\ 999911659) G∑d∣nCndn(mod 999911659)的值。
n n n的范围可以 O ( n ) O\sqrt(n) O(n)枚举因子。对于每个因子可能我们会想用 l u c a s lucas lucas算出这个组合数。但是有 3 3 3个问题。第一, l u c a s lucas lucas要求模数必须是质数。 第二, l u c a s lucas lucas的模数不能太大,因为我们要暴力算 P P P以内的组合数。第三,就算我们算出了G的幂次,也无法直接快速幂。因为 m o d mod mod运算是不能带指数的。
以下记 P = 999911659 P=999911659 P=999911659
学的定理多果然是有好处的。根据扩展欧拉定理, a b = a b m o d ϕ ( n ) ( m o d n ) {a^b}=a^{b\ mod\ \phi(n)}(mod\ n) ab=ab mod ϕ(n)(mod n),要求 g c d ( a , n ) = 1 gcd(a,n)=1 gcd(a,n)=1。所以我们先判断 G G G是不是 P P P的倍数,如是的话就为 0 0 0了。否则,由于 P P P是一个质数,定有 g c d ( a , P ) = 1 gcd(a,P)=1 gcd(a,P)=1。就可以把上面取模了。
又有一个问题, P − 1 P-1 P−1不是一个质数,我们可能要对上面暴力扩展 l u c a s lucas lucas。但是这样 P − 1 P-1 P−1 还是太大了不好做。 (mark一下没学过扩展 l u c a s lucas lucas)
我们把 P − 1 P-1 P−1分解质因数。 999911658 = 2 ∗ 3 ∗ 4679 ∗ 35617 999911658=2*3*4679*35617 999911658=2∗3∗4679∗35617,所以我们把 m o d P − 1 mod\ P-1 mod P−1转化成 m o d mod mod这些互质的质因数。这样根据 C R T CRT CRT,求出符合4个条件的最小正整数解就可以了(记得一边算一边要 m o d ( P − 1 ) mod\ (P-1) mod (P−1))。最后一个快速幂就可以算出答案了。