OI模板 gcd/逆元/同余
Always remember long long!
gcd
inline int gcd(int a, int b){ return (b ? gcd(b, a%b) : a); }
exgcd
求解 a x + b y = 1 ax + by = 1 ax+by=1 的可行解。
inline void exgcd(int &x, int &y, int a, int b){
if(!b){ x = 1, y = 0; return; }
exgcd(x, y, b, a%b);
int z = x; x = y; y = z - a / b * y;
}
a x ≡ 1 ( m o d b ) ax\equiv 1(\bmod~b) ax≡1(mod b)
对于 a x ≡ 1 ( m o d b ) ax\equiv1(\bmod~b) ax≡1(mod b) 的形式,可以转化为 a x + b y = 1 ax+by=1 ax+by=1 进行求解,解出特解 x 0 x_0 x0,通解即为 x 0 + k b x_0+kb x0+kb,最小正整数解 x m i n Z + = ( x 0 m o d b + b ) m o d b x_{min\Bbb{Z^+}}=(x_0\bmod b+b)\mod b xminZ+=(x0modb+b)modb(其实是真正通解的简化版)。
有正整数解时的一些特殊解
有整数解时的一些特殊解
整合
裴蜀定理
a
x
+
b
y
=
c
ax+by=c
ax+by=c (
x
,
y
x,y
x,y 为正整数)成立的充要条件是
g
c
d
(
a
,
b
)
∣
c
gcd(a,b)|c
gcd(a,b)∣c。显然
有理数取余
求解 x ≡ a b ( m o d p ) x \equiv \displaystyle\frac ab~(\bmod~p) x≡ba (mod p)。
- 两边同乘 b b b 得 b x ≡ a ( m o d p ) bx \equiv a~(\bmod~p) bx≡a (mod p)。
- 用 exgcd \texttt{exgcd} exgcd 求得一 x 1 x_1 x1 使得 b x 1 ≡ 1 ( m o d p ) bx_1 \equiv 1~(\bmod~p) bx1≡1 (mod p)
- 所以 b × ( a x 1 ) ≡ a ( m o d p ) b\times(ax_1) \equiv a~(\bmod~p) b×(ax1)≡a (mod p)。
- 此时 a x 1 m o d p ax_1 \bmod p ax1modp 即为答案。
- (当 b m o d p = 0 b \bmod p = 0 bmodp=0 时无解)
int getmod(int a, int b, int p){//要满足 a < p, b < p(不满足取mod)
if(b % p == 0) return -1;
int x, y; exgcd(x, y, b, p);
long long ans = a * (long long)((x + p) % p) % p;
return (int)ans;
}
逆元
费马小定理
a x ≡ 1 ( m o d p ) ax\equiv1~(\bmod~p) ax≡1 (mod p) 则 x ≡ a p − 2 ( m o d p ) x\equiv a^{p-2}~(\bmod~p) x≡ap−2 (mod p),快速幂求解。(仅限 p p p 为质数)
exgcd
a
x
≡
1
(
m
o
d
p
)
ax\equiv1~(\bmod~p)
ax≡1 (mod p) 则有整数
k
k
k 使得
a
x
+
p
k
=
1
ax+pk=1
ax+pk=1,exgcd 求解。(仅限
x
⊥
p
x\perp p
x⊥p 但这个不满足的话就没有逆元了)
int getinv(int x, int p){
int a, k;
exgcd(a, k, x, p);
return (a % p + p) % p;
}
线性递推
初始化
1
−
1
≡
1
(
m
o
d
p
)
1^{-1}\equiv1(\bmod~p)
1−1≡1(mod p)。
令
p
=
k
i
+
r
p=ki+r
p=ki+r(
k
=
⌊
p
i
⌋
k=\lfloor\frac pi\rfloor
k=⌊ip⌋,
r
=
p
m
o
d
i
r=p\bmod i
r=pmodi ),则有:
k
i
+
r
≡
0
(
m
o
d
p
)
k
×
r
−
1
+
i
−
1
≡
0
(
m
o
d
p
)
(
两
边
同
乘
i
−
1
×
r
−
1
)
i
−
1
≡
−
k
×
r
−
1
(
m
o
d
p
)
i
−
1
≡
−
⌊
p
i
⌋
×
(
p
m
o
d
i
)
−
1
(
m
o
d
p
)
(
代
入
)
i
−
1
≡
p
−
⌊
p
i
⌋
×
(
p
m
o
d
i
)
−
1
(
m
o
d
p
)
\begin{aligned} ki+r&\equiv0(\bmod~p)\\ k\times r^{-1}+i^{-1}&\equiv0(\bmod~p)(两边同乘 i^{-1}\times r^{-1})\\ i^{-1}&\equiv-k\times r^{-1}(\bmod~p)\\ i^{-1}&\equiv-\lfloor\frac pi\rfloor\times (p\bmod i)^{-1}(\bmod~p)(代入)\\ i^{-1}&\equiv p-\lfloor\frac pi\rfloor\times (p\bmod i)^{-1}(\bmod~p) \end{aligned}
ki+rk×r−1+i−1i−1i−1i−1≡0(mod p)≡0(mod p)(两边同乘i−1×r−1)≡−k×r−1(mod p)≡−⌊ip⌋×(pmodi)−1(mod p)(代入)≡p−⌊ip⌋×(pmodi)−1(mod p)
因为
p
m
o
d
i
<
i
p\bmod i<i
pmodi<i,所以
(
p
m
o
d
i
)
−
1
(p\bmod i)^{-1}
(pmodi)−1 已经算过了,可以递推。
const int N = 1e7 + 10;
int inv[N];
inline void getinv(int n, int p){
inv[1] = 1;
for(int i = 2; i <= n; ++ i)
inv[i] = p - (long long)p / i * inv[p%i] % p;
//inv[i] = (long long)(p - p / i) * inv[p%i] % p; 其实都行
return;
}
线性阶乘逆元
i
n
v
[
(
i
−
1
)
!
]
=
1
(
i
−
1
)
!
=
i
∗
1
i
!
=
i
∗
i
n
v
[
i
!
]
inv[(i-1)!]=\displaystyle\frac{1}{(i-1)!}=i*\frac{1}{i!}=i*inv[i!]
inv[(i−1)!]=(i−1)!1=i∗i!1=i∗inv[i!]
所以我们可以先算出
i
n
v
[
n
!
]
inv[n!]
inv[n!],然后反向递推即可。