gcd(a,b)=gcd(b,a%b)
a ∗ x 0 + b ∗ y 0 = g c d ( a , b ) a*x_0+b*y_0=gcd(a,b) a∗x0+b∗y0=gcd(a,b)
int exgcd(int a,int b,int &x,int &y) {
if(b==0) {
x=1,y=0;
return a;
}
int x2,y2;
int ret=exgcd(b,a%b,x2,y2);
x=y2;
y=x2-(a/b)*y2;
return ret;
}
g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
⇒ \Rightarrow ⇒ a ⋅ x 1 + b ⋅ y 1 = b ⋅ x 2 + ( a % b ) ⋅ y 2 a·x1+b·y1=b·x2+(a\%b)·y2 a⋅x1+b⋅y1=b⋅x2+(a%b)⋅y2
⇒ \Rightarrow ⇒ a ⋅ x 1 + b ⋅ y 1 = b ⋅ x 2 + ( a − ⌊ a b ⌋ ⋅ b ) ⋅ y 2 a·x1+b·y1=b·x2+(a-\left\lfloor\dfrac{a}{b}\right\rfloor ·b)·y2 a⋅x1+b⋅y1=b⋅x2+(a−⌊ba⌋⋅b)⋅y2
⇒ \Rightarrow ⇒ a ⋅ x 1 + b ⋅ y 1 = a ⋅ y 2 + b ⋅ ( x 2 − ⌊ a b ⌋ ⋅ y 2 ) a·x1+b·y1=a·y2+b·(x2-\left\lfloor\dfrac{a}{b}\right\rfloor·y2) a⋅x1+b⋅y1=a⋅y2+b⋅(x2−⌊ba⌋⋅y2)
⇒ \Rightarrow ⇒ x 1 = y 2 , y 1 = x 2 − ⌊ a b ⌋ ⋅ y 2 x1=y2,y1=x2-\left\lfloor\dfrac{a}{b}\right\rfloor·y2 x1=y2,y1=x2−⌊ba⌋⋅y2
x ′ = x 0 + b g c d k x'=x0+\dfrac{b}{gcd}k x′=x0+gcdbk , , , y ′ = y 0 − a g c d k y'=y0-\dfrac{a}{gcd}k y′=y0−gcdak
1.解方程
2.求逆元
a ⋅ x ≡ 1 ( m o d p ) a ⋅ x − p ⋅ k = 1 a·x≡1(mod\ \ p) \\ a·x-p·k=1 a⋅x≡1(mod p)a⋅x−p⋅k=1
将 p p p视作 b b b, − k -k −k视作 y y y
⇒ a ⋅ x + b ⋅ y = 1 \Rightarrow a·x+b·y=1 ⇒a⋅x+b⋅y=1
解出 x 0 x0 x0用 x ′ = x 0 + p ⋅ h x'=x0+p·h x′=x0+p⋅h求出合适的解
int INV(int a,int p) {
int x,y;
exgcd(a,p,x,y);
x=(x%p+p)%p;
return x;
}
p p p为素数, g c d ( a , p ) = 1 , gcd(a,p)=1, gcd(a,p)=1,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1}≡1(mod\ \ p) ap−1≡1(mod p)
快速幂求逆元
由上 a p − 1 ≡ 1 ( m o d p ) a^{p-1}≡1(mod\ \ p) ap−1≡1(mod p)
⇒ a ⋅ a p − 2 ≡ 1 ( m o d p ) \Rightarrow a·a^{p-2}≡1(mod\ \ p) ⇒a⋅ap−2≡1(mod p)
⇒ a − 1 ≡ a p − 2 ( m o d p ) \Rightarrow a^{-1}≡a^{p-2}(mod\ \ p) ⇒a−1≡ap−2(mod p)
int mod;
LL Pow(int a,int k) {
LL base=a,ret=1;
while(k) {
if(k%2)
ret=(LL)(base*ret)%mod;
base=(LL)(base*base)%mod;
k/=2;
}
return ret;
}
LL INV(int a,int b) {
mod=b;
return Pow(a,b-2);
}
是费马小定理的一般化,等考完还有想法再补补故事
g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,则 a φ ( p ) ≡ 1 ( m o d p ) a^{φ(p)}≡1(mod\ \ p) aφ(p)≡1(mod p)
当 p p p为素数, φ ( p ) = p − 1 φ(p)=p-1 φ(p)=p−1,就成了费马小定理
( p − 1 ) ! ≡ − 1 ( m o d p ) (p−1)!\ ≡−1 (mod \ \ p) (p−1)! ≡−1(mod p) ⇔ \Leftrightarrow ⇔ p p p为素数
一般没什么用吧
为了区分
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) x ≡ a 3 ( m o d m 3 ) … … x ≡ a n ( m o d m n ) \begin{cases}\\ x≡a_1(mod \ \ m_1)\\ x≡a_2(mod \ \ m_2)\\ x≡a_3(mod \ \ m_3)\\ ……\\ x≡a_n(mod \ \ m_n) \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x≡a1(mod m1)x≡a2(mod m2)x≡a3(mod m3)……x≡an(mod mn)
其中 m i m_i mi两两互质,求 x x x的解
使用构造法,令 M = Π m i M=Πm_i M=Πmi
求得 t i ≡ ( M m i ) − 1 ( m o d m i ) t_i≡{\left(\dfrac{M}{m_i}\right)}^{-1} (mod \ \ m_i) ti≡(miM)−1(mod mi)
特解为 x 0 = ∑ a i ⋅ t i ⋅ ( M m i ) x_0=∑a_i·t_i·\left(\dfrac{M}{m_i}\right) x0=∑ai⋅ti⋅(miM)
通解为 x ′ = x 0 + k ⋅ M x'=x_0+k·M x′=x0+k⋅M
LL Exgcd(LL a,LL b,LL &x,LL &y){
if(!b){x=1,y=0;return a;}
LL g=Exgcd(b,a%b,y,x);
y-=(a/b)*x;
return g;
}
int n;
LL a[MAXN+5],m[MAXN+5];
LL Mul(LL x,LL y,LL p){
if(y<0) x=-x,y=-y;
LL ret=0;
while(y){
if(y&1) ret=(ret+x)%p;
x=(x<<1)%p,y>>=1;
}
return ret;
}
LL ExCRT(){
LL M=m[1],x0=a[1],x,y,tmp;
for(int i=2;i<=n;i++){
LL g=Exgcd(M,m[i],x,y);
if((a[i]-x0)%g) return -1;
tmp=m[i]/g,x=Mul(x,(a[i]-x0)/g,tmp),x=(x+tmp)%tmp;
x0+=M*x,M=M/g*m[i],x0%=M;
}
return x0;
}
该部分感谢LSK大佬
证明就把每一个对应部分分出来就行了,易知这是最小单位。
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) x ≡ a 3 ( m o d m 3 ) … … x ≡ a n ( m o d m n ) \begin{cases}\\ x≡a_1(mod \ \ m_1)\\ x≡a_2(mod \ \ m_2)\\ x≡a_3(mod \ \ m_3)\\ ……\\ x≡a_n(mod \ \ m_n) \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧x≡a1(mod m1)x≡a2(mod m2)x≡a3(mod m3)……x≡an(mod mn)
不满足 m i m_i mi两两互质,求 x x x的解
使用逐个求解并合并的方法。
假设我们已经得到了一组特解 x 0 x_0 x0,如何得到一般解呢?
类比上文的孙子定理容易得到 x ′ = x 0 + k ⋅ l c m ( m 1 , m 2..... m n ) x'=x_0+k·lcm(m1,m2.....m_n) x′=x0+k⋅lcm(m1,m2.....mn)
一个一个的向下解
令 M i = l c m ( m 1 , m 2 . . . . . . m i ) M_i=lcm(m_1,m_2......m_i) Mi=lcm(m1,m2......mi)
假如已经得到了前 i i i个方程的解 x = x 0 + k ⋅ M i ⇔ x ≡ x 0 ( m o d M i ) x=x_0+k·M_i\Leftrightarrow x≡x_0(mod\ \ M_i) x=x0+k⋅Mi⇔x≡x0(mod Mi)
现在解第 i + 1 i+1 i+1个方程,也就是再去求一组特解。
可以将问题简化为
{ x ≡ x 0 ( m o d M i ) x ≡ a i + 1 ( m o d m i + 1 ) \begin{cases} x≡x_0(mod\ \ M_i)\\ x≡a_{i+1}(mod\ \ m_{i+1}) \end{cases} {x≡x0(mod Mi)x≡ai+1(mod mi+1)
可以写成
{ x = x 0 + k 1 ⋅ M i x = a i + k 2 ⋅ m i + 1 ⇒ x 0 + k 1 ⋅ M i = a i + k 2 ⋅ m i + 1 ⇒ x 0 − a i = − k 1 ⋅ M i + k 2 ⋅ m i + 1 \begin{cases} x=x_0+k_1·M_i\\ x=a_i+k_2·m_{i+1} \end{cases}\\ \Rightarrow x_0+k_1·M_i=a_i+k_2·m_{i+1}\\ \Rightarrow x_0-a_i=-k_1·M_i+k_2·m_{i+1} {x=x0+k1⋅Mix=ai+k2⋅mi+1⇒x0+k1⋅Mi=ai+k2⋅mi+1⇒x0−ai=−k1⋅Mi+k2⋅mi+1
因为 k 1 k1 k1的正负不重要
x 0 − a i = k 1 ⋅ M i + k 2 ⋅ m i + 1 x_0-a_i=k_1·M_i+k_2·m_{i+1} x0−ai=k1⋅Mi+k2⋅mi+1
直接用拓欧解出一组 k 1 , k 2 k_1,k_2 k1,k2代入方程再更新 M i M_i Mi一直到最后就求出了方程的解。
中途用拓欧解方程时,若 g c d ( M i , m i + 1 ) ∤ x 0 − a i gcd(M_i,m_{i+1})\nmid{x0-a_i} gcd(Mi,mi+1)∤x0−ai则无解
#include
#include
using namespace std;
const int MAXN=int(1e5+5);
int n,M=1,x0=0;
int a[MAXN],m[MAXN];
int exgcd(int a,int b,int &x,int &y) {
if(b==0) {
x=1,y=0;
return a;
}
int x2,y2;
int ret=exgcd(b,a%b,x2,y2);
x=y2;
y=x2-(a/b)*y2;
return ret;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&m[i],&a[i]);
for(int i=1;i<=n;i++) {
int x,y;
int G=exgcd(M,m[i],x,y);
if((x0-a[i])%G) {
puts("No solution");
return 0;
}
x*=(x0-a[i])/G,y*=(x0-a[i])/G;
x0=a[i]+y*m[i];
M=M/G*m[i];
x0=((x0%M)+M)%M;
}
printf("%d",x0);
}
见上
见上
法1:求阶乘逆元反解(见下)
n ! − 1 = ( n − 1 ) ! − 1 ⋅ n − 1 n!^{-1}=(n-1)!^{-1}·n^{-1} n!−1=(n−1)!−1⋅n−1
⇒ n − 1 = n ! − 1 ⋅ ( ( n − 1 ) ! − 1 ) − 1 = n ! − 1 ⋅ ( n − 1 ) ! \Rightarrow n^{-1}=n!^{-1}·((n-1)!^{-1})^{-1}=n!^{-1}·(n-1)! ⇒n−1=n!−1⋅((n−1)!−1)−1=n!−1⋅(n−1)!
int Pow(int a,int k) {
int base=a,ret=1;
while(k) {
if(k%2)
ret=((LL)ret*base)%p;
base=((LL)base*base)%p;
k/=2;
}
return ret;
}
void Prepare() {
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=((LL)fac[i-1]*i)%p;
inv1[n]=Pow(fac[n],p-2);
for(int i=n;i>=1;i--)
inv1[i-1]=((LL)inv1[i]*i)%p;
for(int i=1;i<=n;i++)
inv2[i]=((LL)inv1[i]*fac[i-1])%p;
}
法2:真正的线性筛逆元
若 p = k ⋅ i + d p=k·i+d p=k⋅i+d其中 d = p % i d=p\%i d=p%i
那么 k ⋅ i + d ≡ 0 ( m o d p ) k·i+d≡0(mod\ \ p) k⋅i+d≡0(mod p)
⇒ − k ⋅ i ≡ d \Rightarrow -k·i≡d ⇒−k⋅i≡d
⇒ − k ⋅ i ⋅ i − 1 ⋅ d − 1 ≡ d ⋅ i − 1 ⋅ d − 1 \Rightarrow -k·i·i^{-1}·d^{-1}≡d·i^{-1}·d^{-1} ⇒−k⋅i⋅i−1⋅d−1≡d⋅i−1⋅d−1
⇒ i − 1 ≡ − k ⋅ d − 1 \Rightarrow i^{-1}≡-k·d^{-1} ⇒i−1≡−k⋅d−1
⇒ i − 1 = ( p − k ) ⋅ d − 1 \Rightarrow i^{-1}=(p-k)·d^{-1} ⇒i−1=(p−k)⋅d−1
void Prepare() {
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(p-p/i)*inv[p%i]%p;
}
用定义理解,先求出 ( p − 1 ) ! − 1 {(p-1)!}^{-1} (p−1)!−1(可先求阶乘,或直接用威尔逊定理)
p p p是素数, ( p − 1 ) ! ≡ − 1 ( m o d p ) (p-1)!≡-1(mod\ \ p) (p−1)!≡−1(mod p)
那么 ( p − 1 ) ! − 1 = ( p − 1 ) − 1 {(p-1)!}^{-1}=(p-1)^{-1} (p−1)!−1=(p−1)−1直接快速幂。
或者直接算出 ( p − 1 ) ! (p-1)! (p−1)!反正都要算。
逆元满足结合律即 a − 1 ⋅ b − 1 = ( a b ) − 1 a^{-1}·b^{-1}=(ab)^{-1} a−1⋅b−1=(ab)−1
那么 i n v [ n ] = i n v [ n − 1 ] ⋅ n − 1 inv[n]=inv[n-1]·n^{-1} inv[n]=inv[n−1]⋅n−1
⇒ i n v [ n ] ⋅ n = i n v [ n − 1 ] ⋅ n − 1 ⋅ n = i n v [ n − 1 ] \Rightarrow inv[n]·n=inv[n-1]·n^{-1}·n=inv[n-1] ⇒inv[n]⋅n=inv[n−1]⋅n−1⋅n=inv[n−1]
void Prepare() {
inv[p-1]=Pow(p-1,p-2);
for(int i=p;i>=1;i--)
inv[i-1]=inv[i]*i%p;
}
当然也是可以先求出线性逆元再用线性逆元求出来。
C ( n , m ) = C ( n − 1 , m ) + C ( n − 1 , m − 1 ) C(n,m)=C(n-1,m)+C(n-1,m-1) C(n,m)=C(n−1,m)+C(n−1,m−1)
void Prepare() {
C[0][0]=C[1][0]=C[1][1]=1;
for(int i=1;i<=MAXN;i++)
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
C ( n , m ) % p = C ( n / p , m / p ) ⋅ C ( n % p , m % p ) % p C(n,m)\%p=C(n/p,m/p)·C(n\%p,m\%p)\%p C(n,m)%p=C(n/p,m/p)⋅C(n%p,m%p)%p
可写为 L u c a s ( n , m ) % p = L u c a s ( n / p , m / p ) ∗ C ( n % p , m % p ) % p Lucas(n,m)\%p=Lucas(n/p,m/p)*C(n\%p,m\%p)\%p Lucas(n,m)%p=Lucas(n/p,m/p)∗C(n%p,m%p)%p
1.当 p p p较小
预处理组合数,直接递归到已处理的数
void Prepare() {
C[0][0]=C[1][0]=C[1][1]=1;
for(int i=1;i<=MAXN;i++)
for(int j=1;j<=i;j++)
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
int Lucas(int n,int m) {
if(n<p&&m<p)
return C[n][m];
return Lucas(n/p,m/p)*Lucas(n%p,m%p)%p;
}
3.当 p p p较大
预处理阶乘和阶乘的逆元(见上),对小的部分直接算,大的部分继续递归处理
void Prepare() {
inv[p-1]=Pow(p-1,p-2);
for(int i=p-1;i>=1;i--)
inv[i-1]=inv[i]*i%p;
fac[0]=1;
for(int i=1;i<=p;i++)
fac[i]=fac[i-1]*i%p;
}
int C(int n,int m) {
if(n<m)
return 0;
return ((fac[n]*inv[m])%p*inv[n-m])%p;
}
int Lucas(int n,int m) {
if(n<p&&m<p)
return C(n,m);
return Lucas(n/p,m/p)*C(n%p,m%p)%p;
}
组合意义(经典例子):
1.括号匹配( n n n对括号的合法匹配数)
2.走地图(从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , n ) (n,n) (n,n)并且不越过 y = x y=x y=x的方案数)
公式: h ( n ) = C ( 2 n , n ) n + 1 h(n)=\dfrac{C(2n,n)}{n+1} h(n)=n+1C(2n,n)
推导:在一个 ( n , n ) (n,n) (n,n)的地图里,从 ( 0 , 0 ) (0,0) (0,0)走到 ( n , n ) (n,n) (n,n)的方案数为 C ( 2 n , n ) C(2n,n) C(2n,n)
要求不越过 y = x y=x y=x,那么求越过的取反。
越过的就是一定经过 y = x + 1 y=x+1 y=x+1的
观察到这样的走法等价于从 ( − 1 , 1 ) (-1,1) (−1,1)走到 ( n , n ) (n,n) (n,n)的方案数(将地图外的部分沿 y = x + 1 y=x+1 y=x+1翻折回来)。
图示