『数学笔记』提高组部分

· · 算法·理论

一些闲话

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

正文部分

因数

因数(Divisor),也称为约数,是指整数 a 除以整数 bb \not= 0)所得的商为整数且没有余数,此时称 ba 的因数,ab 的倍数。\ 若某一个数 pa 的因数,且 p 为质数,则称 pa 的质因数。

唯一分解定理:

每一个大于 1 的自然数都可以唯一分解为质数的乘积,且这种分解不考虑质数排列顺序时是唯一的。‌‌\ 也就是说,我们可以将大于 1 的自然数 N 写成以下形式:

N = \prod_{i=1}^{k}{p_i}^{\alpha_i}

其中,k 为质因数个数,p_i 为第 i 个质因数(一般来说从小到大),\alpha_i 为该质因数的次数。

实际上,一个大于 1 的自然数的各个因数就是由它的各个质因数组合及次方而来。\ 由乘法原理可得,我们可以把因数个数写成以下形式:

N = \prod_{i=1}^{k}{(\alpha_i+1)}

实际上 N 的因数是在 p_1^{\alpha_1},......,p_k^{\alpha_k} 每一个的因数中分别挑一个相乘得来。\ 因数之和也就是各个质因数的各个次方相加后相乘。

N = \prod_{i=1}^{k}{\sum_{j=0}^{\alpha_i}p_i^j}

GCD 与 LCM

最大公约数(Greatest Common Divisor, GCD)是指两个或多个整数共有约数中最大的一个。\ 最小公倍数(Least Common Multiple, LCM)是指两个或多个整数共有倍数中最小的一个。\ 他们有如下性质:

逆元与同余

inv[1] = 1;
for(int i = 2;i <= n;++i) inv[i] = (p - p / i) * inv[p % i] % p;

欧拉函数与欧拉定理

定义欧拉函数 \phi(n) 为不超过 n 且与 n 互质的正整数个数,为积性函数。

欧拉函数有以下性质

代码:

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;
}
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,则 x^{\phi(y)} \equiv 1\pmod y。\ 带入欧拉定理可得费马小定理

扩展欧拉定理:

可用于降幂,形式如下:

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}

代码:

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,b 均为整数,则有整数 x,y 使得 ax + by = \gcd(a,b)。\ 它有一堆推论或推广:

威尔逊定理

p 为整数,则 p 可以整除 (p-1)! + 1。\ 这个定理可以改写成以下形式:

推论:

组合与排列

排列:P_n^r=\frac{n!}{(n-r)!}。\ 组合:C_n^r=\frac{n!}{(n-r)!r!}。\ 组合数有以下性质:

多重集的排列和组合:

  1. 无限多重集的排列:\
  2. 有限多重集的排列:\
  3. 有限多重集的组合:\

二项式系数:

二项式系数有两种计算方法:

  1. 递推:C_n^r = C_{n-1}^{r-1} + C_{n-1}^{r}
  2. 逆元:C_n^r \bmod m = \

卢卡斯定理:

对于质数 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. $H_n = C_{2n}^n - C_{2n}^{n-1}$。 2. $H_n = \frac{1}{n+1}C_{2n}^n$。 3. $H_n = \frac{4n+2}{n+1}H_{n-1}$。\ 注意:$2$ 和 $3$ 都有大数除法,$n$ 非常大,需要进行取模操作,直接做除法会损失精度,需要转换为逆元再取模。 ### 圆排列: 是排列的一种,指 $m$ 个数中选 $n$ 个不同的元素排列成一个环形, 无头无尾。两个循环排列相同当且仅当所取元素的个数相同并且元素取法一致,在环上的排列顺序一致,记作 $Q_n^m$。\ $Q_n^n = \frac{A_n^n}{n}$。\ $Q_n^m = C_n^m Q_m^m = \frac{n!}{m(n-m)!}

错位排列:

$D_1 = 0,D_2 = 2$。\ $D_n = (n-1)(D_{n-1} + D_{n-2})$。 ## 不定方程与同余方程 ### 二元线性丢番图方程(不定方程): 二元线性丢番图方程形如 $ax + by = c$。\ $ax + by = c$ 有解的充分必要条件是 $d = \gcd(a,b)$ 能整除 $c$。\ 如果 $(x_0,y_0)$ 是方程的一个特解,通解:\ $x = x_0 + \frac{bn}{d}$,$y = y_0 - \frac{an}{d}$。\ 其中 $n$ 是任意整数。\ 求解一元线性同余方程等价于求解二元线性丢番图方程:\ $ax \equiv b \pmod m$ 表示 $ax - b$ 是 $m$ 的倍数,设为 $-y$ 倍,则有 $ax + my = b$。\ 这就是二元线性丢番图方程。 ### 扩展欧几里得算法: 是欧几里得算法的扩展,用于求解两个整数 $a,b$ 的最大公约数,同时找到满足裴蜀等式 $ax + by = \gcd(a, b)$ 的整数解 $x$ 和 $y$。\ 具体过程如下: - 递归过程‌:若 $b \not= 0$,递归计算 $gcd(b,a \bmod b)$ ,并利用当前层的商 $q = \frac{a}{b}$ 更新 $x$ 和 $y$ 的值。 - 下一层的解 $x_1$ 和 $y_1$ 满足 $bx_1 + (a \bmod b)\times y_1 = \gcd(b,a \bmod b)$。 - 递归终止条件‌:当 $b = 0$ 时,此时 $x = 1,y = 0$。 - 当前层的解 $x = 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; } ``` ### 同余方程组-中国剩余定理 $$ \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} $$ 其中对于任意满足 $1 \le i,j \le n$ 且 $i \not= j$ 的 $i,j$,**保证** $\gcd(m_i,m_j) = 1$。 1. 计算所有模数的积 $M$。\ $M = \prod_{i=1}^{n}m_i$。 2. 计算第 $i$ 个方程的 $c_i$。\ $c_i = \frac{M}{m_i}$。 3. 计算 ${c_i}^{-1} \pmod {m_i}$。 4. $x = \sum_{i=1}^nr_ic_ic_i^{-1} \pmod M$。 ``` 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; } ``` ### 同余方程组-扩展中国剩余定理 $$ \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} $$ 其中对于任意满足 $1 \le i,j \le n$ 且 $i \not= j$ 的 $i,j$,**不保证** $\gcd(m_i,m_j) = 1$。\ 对于模数不互质的情况,我们可以通过合并相邻两组方程的方式来逐步得到解。\ $x \equiv r_i \pmod {m_i},x \equiv r_j \pmod {m_j}$。\ 转化得:$r_i - y_im_i = x,r_j - y_jm_j = x$。\ 所以:$r_i - y_im_i = r_j - y_jm_j$。\ 所以:$r_i - r_j = y_im_i - y_jm_j$。\ 现在,$r_i,r_j,m_i,m_j$ 都是已知的,只有 $y_i,y_j$ 是未知的。我们不妨把它看成是关于 $y_i,y_j$ 的不定方程。\ 那么根据裴蜀定理,$(r_i-r_j) \bmod \gcd(m_i,m_j) \not= 0$,则原方程组无解。\ 由扩展欧几里得算法,两个方程可以等价合并为以下形式:\ $x \equiv r \pmod m$。\ 其中 $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](https://www.luogu.com.cn/user/1275540),是 TA 给我讲的裴蜀定理以及告诉了我很多 LaTeX 的公式。 - 感谢神犇 唐望(数竞生,所以没有 luogu 账号),是 TA 给我证明了裴蜀定理的推广形式。 - 感谢神犇 [lijunxi2026](https://www.luogu.com.cn/user/1078578),是 TA 借了我整理笔记的草稿纸。 - 感谢神犇 [ligenC2026](https://www.luogu.com.cn/user/1268368),是 TA 在我整理笔记时借了我修正带,成功避免了浪费草稿纸,以及在某些地方感谢我。