专栏文章

『数学笔记』提高组部分

算法·理论参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@mior8x5b
此快照首次捕获于
2025/12/02 23:50
3 个月前
此快照最后确认于
2025/12/02 23:50
3 个月前
查看原文

一些闲话

也是学上数学了好吧。
要不是要听写,正常人谁整理笔记啊。
膜拜神犇 Hootime 和 TA 的 npy chenyuexiC2026

正文部分

因数

因数(Divisor),也称为约数,是指整数 aa 除以整数 bbb0b \not= 0)所得的商为整数且没有余数,此时称 bbaa 的因数,aabb 的倍数。
若某一个数 ppaa 的因数,且 pp 为质数,则称 ppaa 的质因数。

唯一分解定理:

每一个大于 11 的自然数都可以唯一分解为质数的乘积,且这种分解不考虑质数排列顺序时是唯一的。‌‌
也就是说,我们可以将大于 11 的自然数 NN 写成以下形式:
N=i=1kpiαiN = \prod_{i=1}^{k}{p_i}^{\alpha_i}
其中,kk 为质因数个数,pip_i 为第 ii 个质因数(一般来说从小到大),αi\alpha_i 为该质因数的次数。
实际上,一个大于 11 的自然数的各个因数就是由它的各个质因数组合及次方而来。
由乘法原理可得,我们可以把因数个数写成以下形式:
N=i=1k(αi+1)N = \prod_{i=1}^{k}{(\alpha_i+1)}
实际上 NN 的因数是在 p1α1,......,pkαkp_1^{\alpha_1},......,p_k^{\alpha_k} 每一个的因数中分别挑一个相乘得来。
因数之和也就是各个质因数的各个次方相加后相乘。
N=i=1kj=0αipijN = \prod_{i=1}^{k}{\sum_{j=0}^{\alpha_i}p_i^j}

GCD 与 LCM

最大公约数(Greatest Common Divisor, GCD)是指两个或多个整数共有约数中最大的一个。
最小公倍数(Least Common Multiple, LCM)是指两个或多个整数共有倍数中最小的一个。
他们有如下性质:
  • gcd(a,b)=gcd(a,a+b)=gcd(a,ka+b)=gcd(a+cb,b)\gcd(a,b) = \gcd(a,a+b) = \gcd(a,ka + b) = \gcd(a+cb,b)
  • gcd(ka,kb)=k×gcd(a,b)\gcd(ka,kb) = k \times\gcd(a,b)
  • lcm(a,b)×gcd(a,b)=ablcm(a,b) \times \gcd(a,b) = ab

逆元与同余

  • 同余:记 amodm=bmodma \bmod m = b \bmod mab(modm)a \equiv b \pmod m
  • 逆元:记满足 gcd(a,m)=1,ax1(modm)\gcd(a,m) = 1,ax \equiv 1 \pmod m 的一个解 xxaamm 的逆,记为 a1a^{-1}
  • 费马小定理:若 a,na,n 互质,则由 an11(modn)a^{n-1} \equiv 1\pmod n
    an2modna^{n-2} \bmod n 就是 aann 的逆。
  • 递推式i1(modp)=(ppi)×(pmodi)1modpi^{-1} \pmod p = (p - \frac{p}{i}) \times (p \bmod i)^{-1} \mod p
CPP
inv[1] = 1;
for(int i = 2;i <= n;++i) inv[i] = (p - p / i) * inv[p % i] % p;

欧拉函数与欧拉定理

定义欧拉函数 ϕ(n)\phi(n) 为不超过 nn 且与 nn 互质的正整数个数,为积性函数。
  • 积性函数:若 gcd(x,y)=1\gcd(x,y) = 1f(xy)=f(x)×f(y)f(xy) = f(x) \times f(y),则称 f(n)f(n) 为积性函数。
欧拉函数有以下性质
  • pp 为质数,则 ϕ(p)=p1\phi(p) = p-1,且 ϕ(pk)=(p1)×pk1\phi(p^k) = (p-1) \times p^{k-1}
  • ϕ(n)=n×i=1s(pi1pi)\phi(n) = n \times \prod_{i=1}^{s}{(\frac{p_i-1}{p_i})},其中 ssnn 的因数数量,pip_i 为第 ii 个因数(一般来说从小到大)。
代码:
CPP
int phi(int p){
    int ans = p;
    for(int i = 2;i * i <= p;++i){
        if(p % i == 0){
            while(p % i == 0) p /= i;
            ans /= i,ans *= (i-1);
        }
    }
	if(p > 1) ans /= p,ans *= (p - 1);
    return ans;
}
CPP
void euler_sieve(){
    for(int i = 0;i <= n;++i) phi[i] = i;
    cnt = 0;
    for(int i = 2;i <= n;++i){
        if(phi[i] == i){//如果i是质数
            primes[cnt++] = i;
            phi[i] = i - 1;// 质数的欧拉函数值是自身减1
        }
        for(int j = 0;j < cnt && primes[j] * i <= n;++j){//遍历已找到的质数
            if(i % primes[j] == 0){
                // i能被primes[j]整除,说明primes[j]是i的最小质因子
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            }
			else phi[i * primes[j]] = phi[i] * (primes[j] - 1);
			// i不能被primes[j]整除,primes[j]是i*primes[j]的最小质因子
        }
    }
}

欧拉定理:

gcd(x,y)=1\gcd(x,y) = 1,则 xϕ(y)1(mody)x^{\phi(y)} \equiv 1\pmod y
带入欧拉定理可得费马小定理
  • 费马小定理:若 yy 为质数,则 xy11(mody)x^{y-1} \equiv 1\pmod y

扩展欧拉定理:

可用于降幂,形式如下:
abmodm{abmodmb<ϕ(m),abmodϕ(m)+ϕ(m)modmbϕ(m) & gcd(a,m)1,abmodϕ(m)modmgcd(a,m)=1.a^b \mod m \equiv \begin{cases} a^b \mod m & b < \phi(m), \\ a^{b \mod \phi(m) + \phi(m)} \mod m & b \geq \phi(m) \text{ \& } \gcd(a, m) \neq 1, \\ a^{b \mod \phi(m)} \mod m & \gcd(a, m) = 1. \end{cases}
代码:
CPP
ph = phi(m);
ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9'){
    x *= 10,x += (ch - '0');
    if(x >= ph) x = x % ph,fl = 1;
    ch = getchar();
}
if(fl == 1) x += ph;
cout << Fastpow(a,x,m) % m;

裴蜀定理

如果 a,ba,b 均为整数,则有整数 x,yx,y 使得 ax+by=gcd(a,b)ax + by = \gcd(a,b)
它有一堆推论或推广:
  • a,ba,b 互质当且仅当存在整数 x,yx,y,使得 ax+by=1ax + by = 1
  • 一定存在整数 x,yx,y,满足 ax+by=gcd(a,b)×nax + by = \gcd(a,b) \times n
  • 一定存在整数 x1,......,xnx_1,......,x_n,满足 i=1naixi=gcd(a1,......,an)\sum_{i = 1}^n a_ix_i = \gcd(a_1,......,a_n)

威尔逊定理

pp 为整数,则 pp 可以整除 (p1)!+1(p-1)! + 1
这个定理可以改写成以下形式:
  • [(p1)!+1]modp=0[(p-1)!+1] \bmod p = 0
  • (p1)!modp=1(p-1)! \bmod p = 1
  • (p1)!=pq1(p-1)! = pq - 1,且 qq 为正整数。
  • (p1)!1(modp)(p-1)! \equiv -1 \pmod p,这也是 pp 为质数的充要条件。
推论:
  • pp 为质数,则 (p1)!+10(modp)(p-1)!+1 \equiv 0 \pmod p
  • pp 为大于 44 的合数,则 (p1)!0(modp)(p-1)! \equiv 0 \pmod p

组合与排列

排列:Pnr=n!(nr)!P_n^r=\frac{n!}{(n-r)!}
组合:Cnr=n!(nr)!r!C_n^r=\frac{n!}{(n-r)!r!}
组合数有以下性质:
  • Cnr=CnnrC_n^r=C_n^{n-r}
  • Cnr=Cn1r1+Cn1rC_n^r=C_{n-1}^{r-1} + C_{n-1}^{r}(帕斯卡公式)。
  • i=0nCni=2n\sum_{i=0}^n C_n^i = 2^n

多重集的排列和组合:

  1. 无限多重集的排列:
    SS 是一个多重集,它有 kk 个不同的元素,每个元素都有无穷重复个数,SSrr 排列个数为 krk^r
  2. 有限多重集的排列:
    SS 是一个多重集,它有 kk 个不同的元素,每个元素的重数分别为 n1,n2,,nkn_1,n_2,…,n_kSS 的大小是 n=Σi=1knin = \Sigma_{i=1}^k n_i,则 SSnn 排列个数为 n!i=1k(ni!)\frac{n!}{\prod_{i=1}^{k} (n_i!)}
  3. 有限多重集的组合:
    SS 是一个多重集,它有 kk 个不同的元素,每个元素都有无穷重复个数,那么 SSrr 组合为 Cr+k1r=Ck1rC_{r+k-1}^r = C_{k-1}^r

二项式系数:

二项式系数有两种计算方法:
  1. 递推:Cnr=Cn1r1+Cn1rC_n^r = C_{n-1}^{r-1} + C_{n-1}^{r}
  2. 逆元:Cnrmodm=C_n^r \bmod m =
    n!r!(nr)!modm=(n!modm)×((r!)1modm)×((nr)!)1modm)modm\frac{n!}{r!(n-r)!} \bmod m = (n! \bmod m) \times ((r!)^{−1} \bmod m) \times ((n − r)!)^{−1} \bmod m) \bmod m

卢卡斯定理:

对于质数 mm
CnrmodmCnmodmrmodm×CnmrmC_n^r \bmod m \equiv C_{n \mod m}^{r \mod m} \times C_{\frac{n}{m}}^{\frac{r}{m}}
代码实现如下:
CPP
void init(){
    for(int i = 1;i <= 19491001;++i) f[i] = (f[i-1] * i) % MOD;
    for(int i = 2;i <= 19491001;++i) r[i] = (MOD - MOD / i) * r[MOD % i] % MOD;
}
int C(int n,int k){//组合数 C(n, k)
    if(k < 0 || k > n) return 0;
    if(k == 0 || k == n) return 1;
    if(k > n - k) k = n - k;
    int q1 = f[n],q2 = (f[k] * f[n-k]) % MOD;
    return q1 * r[q2] % MOD;
}
int lucas(int n,int k){//Lucas 定理计算 C(n, k) mod MOD
	//C(n,m) % p = C(n/p,m/p) * C(n%p,m%p) % p 
    if(k < 0 || k > n) return 0;
    if(k == 0) return 1;
    int res = 1;
    while(n || k){
        int ni = n % MOD,ki = k % MOD;
        if(ki > ni) return 0;
        res = (res * C(ni, ki)) % MOD;
        n /= MOD,k /= MOD;
    }
    return res;
}

容斥原理

集合的并等于集合的交的交错和(奇正偶负)。
集合的交等于全集的交减去补集的并。

Catalan 数

通项公式:
  1. Hn=C2nnC2nn1H_n = C_{2n}^n - C_{2n}^{n-1}
  2. Hn=1n+1C2nnH_n = \frac{1}{n+1}C_{2n}^n
  3. Hn=4n+2n+1Hn1H_n = \frac{4n+2}{n+1}H_{n-1}
    注意:2233 都有大数除法,nn 非常大,需要进行取模操作,直接做除法会损失精度,需要转换为逆元再取模。

圆排列:

是排列的一种,指 mm 个数中选 nn 个不同的元素排列成一个环形, 无头无尾。两个循环排列相同当且仅当所取元素的个数相同并且元素取法一致,在环上的排列顺序一致,记作 QnmQ_n^m
Qnn=AnnnQ_n^n = \frac{A_n^n}{n}
Qnm=CnmQmm=n!m(nm)!Q_n^m = C_n^m Q_m^m = \frac{n!}{m(n-m)!}

错位排列:

nn 个有序的元素应有 n!n! 个不同的排列,如若一个排列使得所有的元素不在原来的位置上,则称这个排列为错排。
D1=0,D2=2D_1 = 0,D_2 = 2
Dn=(n1)(Dn1+Dn2)D_n = (n-1)(D_{n-1} + D_{n-2})

不定方程与同余方程

二元线性丢番图方程(不定方程):

二元线性丢番图方程形如 ax+by=cax + by = c
ax+by=cax + by = c 有解的充分必要条件是 d=gcd(a,b)d = \gcd(a,b) 能整除 cc
如果 (x0,y0)(x_0,y_0) 是方程的一个特解,通解:
x=x0+bndx = x_0 + \frac{bn}{d}y=y0andy = y_0 - \frac{an}{d}
其中 nn 是任意整数。
求解一元线性同余方程等价于求解二元线性丢番图方程:
axb(modm)ax \equiv b \pmod m 表示 axbax - bmm 的倍数,设为 y-y 倍,则有 ax+my=bax + my = b
这就是二元线性丢番图方程。

扩展欧几里得算法:

是欧几里得算法的扩展,用于求解两个整数 a,ba,b 的最大公约数,同时找到满足裴蜀等式 ax+by=gcd(a,b)ax + by = \gcd(a, b) 的整数解 xxyy
具体过程如下:
  • 递归过程‌:若 b0b \not= 0,递归计算 gcd(b,amodb)gcd(b,a \bmod b) ,并利用当前层的商 q=abq = \frac{a}{b} 更新 xxyy 的值。
  • 下一层的解 x1x_1y1y_1 满足 bx1+(amodb)×y1=gcd(b,amodb)bx_1 + (a \bmod b)\times y_1 = \gcd(b,a \bmod b)
  • 递归终止条件‌:当 b=0b = 0 时,此时 x=1,y=0x = 1,y = 0
  • 当前层的解 x=y1,y=x1qy1x = y_1,y = x_1 - q * y_1
‌ 代码实现如下:
CPP
int exgcd(int a,int b,int &x,int &y){
	if(b == 0){
		x = 1,y = 0;
		return a;
	}
	int gcd = exgcd(b,a%b,y,x);
	y -= a / b * x;
	return gcd;
}

同余方程组-中国剩余定理

{xr1(modm1),xr2(modm2),......xrn(modmn).\begin{cases} x \equiv r_1 \pmod {m_1}, \\ x \equiv r_2 \pmod {m_2}, \\ ...... \\ x \equiv r_n \pmod {m_n}. \end{cases}
其中对于任意满足 1i,jn1 \le i,j \le niji \not= ji,ji,j保证 gcd(mi,mj)=1\gcd(m_i,m_j) = 1
  1. 计算所有模数的积 MM
    M=i=1nmiM = \prod_{i=1}^{n}m_i
  2. 计算第 ii 个方程的 cic_i
    ci=Mmic_i = \frac{M}{m_i}
  3. 计算 ci1(modmi){c_i}^{-1} \pmod {m_i}
  4. x=i=1nricici1(modM)x = \sum_{i=1}^nr_ic_ic_i^{-1} \pmod M
CPP
int dickduck(int x,int y,int Mod){//龟速乘防止爆 long long
	int ans = 0;
	while(y >= 1){
		if(y & 1) ans = (ans + x) % Mod;
		y = y >> 1,x = (x << 1) % Mod;
	}
	return ans;
}
int exgcd(int a,int b,int &x,int &y){//扩展欧几里得算法
    if(b == 0){
        x = 1,y = 0;
        return a;
    }
    int gcd1 = exgcd(b,a % b,y,x); //递归计算并交换 x,y
    y -= a / b * x;
    return gcd1;
}
int modinv(int a,int m){//模逆元(不能保证 m 是质数,不能用快速幂) 
    int x,y;
    int gcd1 = exgcd(a,m,x,y);
    if(gcd1 != 1) return -1;//无逆元
    return (x % m + m) % m;
}
int cr(int n){//中国剩余定理
    int M = 1,x = 0;
    for(int i = 1;i <= n;++i) M *= mm[i];         //1.计算总模数 M
    for(int i = 1;i <= n;++i){
        int Mi = M / mm[i];                       // Mi(其它模数之积)
        int ti = modinv(Mi,mm[i]);                // ti(Mi 在 mod mm[i] 下的逆元)
        if (ti < 0) return -1;                    // 逆元不存在
        x = (x + dickduck(aa[i],dickduck(Mi,ti,M),M)) % M;             // 累加
    }
    return (x + M) % M;
}

同余方程组-扩展中国剩余定理

{xr1(modm1),xr2(modm2),......xrn(modmn).\begin{cases} x \equiv r_1 \pmod {m_1}, \\ x \equiv r_2 \pmod {m_2}, \\ ...... \\ x \equiv r_n \pmod {m_n}. \end{cases}
其中对于任意满足 1i,jn1 \le i,j \le niji \not= ji,ji,j不保证 gcd(mi,mj)=1\gcd(m_i,m_j) = 1
对于模数不互质的情况,我们可以通过合并相邻两组方程的方式来逐步得到解。
xri(modmi),xrj(modmj)x \equiv r_i \pmod {m_i},x \equiv r_j \pmod {m_j}
转化得:riyimi=x,rjyjmj=xr_i - y_im_i = x,r_j - y_jm_j = x
所以:riyimi=rjyjmjr_i - y_im_i = r_j - y_jm_j
所以:rirj=yimiyjmjr_i - r_j = y_im_i - y_jm_j
现在,ri,rj,mi,mjr_i,r_j,m_i,m_j 都是已知的,只有 yi,yjy_i,y_j 是未知的。我们不妨把它看成是关于 yi,yjy_i,y_j 的不定方程。
那么根据裴蜀定理,(rirj)modgcd(mi,mj)0(r_i-r_j) \bmod \gcd(m_i,m_j) \not= 0,则原方程组无解。
由扩展欧几里得算法,两个方程可以等价合并为以下形式:
xr(modm)x \equiv r \pmod m
其中 p=p0×rjrigcd,r=mip+ri,m=lcm(mi,mj)p = p_0 \times \frac{r_j-r_i}{gcd},r = m_ip+r_i,m = lcm(m_i,m_j)
代码:
CPP
// 龟速乘(避免溢出):计算 (x * y) % Mod
int dickduck(int x,int y,int Mod){
    int ans = 0;
    while(y >= 1){
        if(y & 1) ans = (ans + x) % Mod;
        y = y >> 1,x = (x << 1) % Mod;
    }
    return ans;
}
// 扩展欧几里得算法:求解 ax + by = gcd(a, b)
int exgcd(int a,int b,int &x,int &y){
    if(b == 0){
        x = 1,y = 0;
        return a;
    }
    int gcd1 = exgcd(b,a % b,y,x);
    y -= a / b * x;
    return gcd1;
}
// 扩展中国剩余定理(EXCRT)核心实现
int excr(int n){
    int m1 = mm[1],r1 = aa[1];
    r1 = (r1 % m1 + m1) % m1;                        //初始化解为第一个方程的最小非负整数
    for (int i = 2;i <= n;++i){
        int m2 = mm[i],r2 = aa[i],p,q;
        r2 = (r2 % m2 + m2) % m2;                    //调整r2为模m2的最小非负整数
        int d = exgcd(m1,m2,p,q);                    //求gcd(m1,m2)和系数p,q
        if((r2 - r1) % d != 0) return -1;            //检查解的存在性
        int M = m2 / d;                              //计算调整后的模数
        int t1 = ((r2 - r1) / d + M) % M,t2 = (p % M) + M;
        int k0 = dickduck(t1,t2,M);
        k0 = (k0 % M + M) % M;                       //调整为最小非负整数
        int lcm = (m1 / d) * m2;                     //计算最小公倍数
        r1 = (r1 + dickduck(m1,k0,lcm) + lcm) % lcm; //新解
        r1 = (r1 % lcm + lcm) % lcm;                 //调整r1为最小非负
        m1 = lcm;                                    //更新模数为新的最小公倍数
    }
    return (r1 % m1 + m1) % m1;                      //返回最终解(最小非负整数)
}

感谢名单

  • 感谢神犇 Hootime,是 TA 给我讲的裴蜀定理以及告诉了我很多 LaTeX 的公式。
  • 感谢神犇 唐望(数竞生,所以没有 luogu 账号),是 TA 给我证明了裴蜀定理的推广形式。
  • 感谢神犇 lijunxi2026,是 TA 借了我整理笔记的草稿纸。
  • 感谢神犇 ligenC2026,是 TA 在我整理笔记时借了我修正带,成功避免了浪费草稿纸,以及在某些地方感谢我。

评论

1 条评论,欢迎与作者交流。

正在加载评论...