CSP 数学基础

ollie

2019-11-08 10:53:21

Personal

## CSP 数学基础 ### 整理&修改—— ollie ![img](https://cdn.luogu.com.cn/upload/pic/5010.png) #### 说明:蒟蒻只是整理,供自己和同学学习,部分转载其他博客,有一些链接丢失,如果有补充请在评论区@,会补上链接。 #### 你可能会好奇为啥要转载,因为我懒的一个一个找了。。。 - 取模相关 - 素数判断 - GCD/EXGCD - 欧拉函数 - 欧拉定理 - 扩展欧拉定理 - 逆元 - 同余公式 - 逆元求法 - CRT/EXCRT - 线性代数 - 快速幂 - *矩阵相关 - *高斯消元 - 组合数学 - 卡特兰数 - 排列组合 - 二项式定理 - 杨辉三角 - Fibonacci数列 - *Lucas定理 - 数学期望 - [原博客(目录)]( https://blog.csdn.net/M_oisture/article/details/83820851 ) 先来一个跟我讲的毫无干系的[例题](https://www.luogu.org/problem/P1403) ### 一、取模相关 - `n%p`所得结果的正负由`n`决定,与`p`无关。如:`7%4=3`,`-7%4=-3`,`-7%-4=-3` - 欧拉定理(后面会细讲) $$ α^{\varphi(p)}≡1mod(p) $$ - 当p为质数的特殊情况 $$ α^{p-1}≡1 $$ - Noip2014式取模, 如果f(x) = 0, 那么f(x) % p = 0, 在式子中带入数字看脸计算。 - 常数较小的取模方式 ### 二、素数判断 1:一般筛法(埃拉托斯特尼筛法): ​ 基本思想:素数的倍数一定不是素数 ​ ~~应该没人用咱就跳过吧~~ ```cpp #include<cstring> #include<cmath> const int MAXN=1000010;//范围 bool prime[MAXN]; void make_prime() { memset(prime,true,sizeof(prime)); prime[0]=prime[1]=false; int t=sqrt(MAXN); for(register int i=2;i<=t;i++) { if(prime[i]) { for(register int j=2*i;j<MAXN,j+=i) { prime[j]=false; } } } return; } ``` 此方法的复杂度为 $$ O(\sum_{p<n}\frac{n}{p})=O(nloglogn) $$ 2:线性筛(优化) 原理:初始时,假设全部都是素数,当找到一个素数时,显然这个素数乘上另外一个数之后都是合数,把这些合数都筛掉,即算法名字的由来。 ```cpp #include<iostream> using namespace std; const int N = 200000; int prime[N] , num_prime; int isNotPrime[N] = {1, 1};//先将0,1排除,合数 int main() { for(int i = 2 ; i <= N ; i ++) { if(! isNotPrime[i]) //isNotPrime==0,即不是合数 prime[++num_prime]=i;//素数++,这不是关键1 //关键处1 /*i为合数时也要参与循环*/ for(int j = 1; j <= num_prime&&i * prime[j] <= N ; j ++) /*i乘以其它质数=合数,且质数比自己小或者是自己(已经求出来了),所以不可能重复*/ { isNotPrime[i * prime[j]] = 1;//乘以其它素数得到的一定是合数,之所以不重复是因为质数间不等 if( !(i % prime[j] ) ) //关键处2 如果i是p的倍数,退出循环,这样可以保证一个数只会被最小素因子筛到一次,prime是从小到大枚举的,1067=11*97, break; } } return 0; } ``` 先,先明确一个条件,任何合数都能表示成一系列素数的积。(为啥???) $4 = 2 * 2$ , $12 = 2 * 3 * 3$, ....... 不管 i 是否是素数,都会执行到“关键处1”( 别看错啦), ①如果 i 都是是素数的话,那简单,一个大的素数 i 乘以不大于 i 的素数,这样筛除的数跟之前的是不会重复的。筛出的数都是 N=p1*p2的形式, p1,p2之间不相等 ( 素数不会与其他数相等 ) $77=7*11$ , 即 $77$ 只能化简成这个形式且唯一 ( ~~这要是不懂那我也木得门~~ ) ②如果 i 是合数,此时 i 可以表示成递增素数相乘 $i=p1* p2 * ...*pn$, $pi$都是素数$(2\leq i\leq n)$, $pi\leq pj ( i\leq j )$ p1是最小的系数。 根据“关键处2”的定义,当$p1==prime[j]$ 的时候,筛除就终止了,也就是说,只能筛出不大于p1的质数。 我们可以直观地举个例子。$i(235)=2*3*5 = 5 * 47$ $for\,\, j=1 -> num\,prime j = 1,2,3,4,5,....$ 此时能筛除 $2i(470)$ ,不能筛除 $3i(705)$ 如果能筛除$3i$ 的话,当 $i'$ 等于 $i'=335$ 时,筛除$2 * i'(705)$就和前面重复了。 所以只能枚举到最小质因子 ##### 质数应用 - 用来取模 - ~~用来装 B~~ [P2312 解方程](https://www.luogu.org/problem/P2312) ### 三、GCD&EXGCD #### 1 . gcd: 欧几里得算法,即辗转相除法,简称gcd,用于计算两个数的最大公约数。 公式 : $$ gcd(a,b)=gcd(b,a mod b) ,b≠0 $$ 啥?证明?能用就行要什么证明 咳咳,让我们来严格的证明一下 > 若 $a < b$,以上等式显然成立(~~你这要是不会我也没办法~~)#1 > > 若 $a \geq b$, 设$a = q *b + r,0\leq r<b$, 即r=a%b 。对于$a$,$b$的任意公约数$d$,因为$d|a$(ps:这个符号的意思是d是a的约数),$d|b$,所以$d|(q * b)$, 可得$d|(a-q*b)$#2,即$d|r$,$d$也是$r$的约数。反之亦成立。所以,$a$,$b$的公约数集合和$b$,$a mod b$的公约数集合相同,他们的最大公约数自然也相同。 > >     证毕 ~#1解释:$gcd(10,20)=gcd(20,10\bmod20)=gcd(20,10)$[~~竟然看这个~~] ~#2解释:为什么?因为$d|a$,$d|b$可以写成$a=k1*d,q*b=k2*d$,相减得$a-q*b=(k1-k2)*d$即是d的倍数 上代码(~~我知道你们只想要这个~~) ```cpp int gcd(int a,int b){ return b?gcd(b,a%b):a; } ``` ##### 补充: **如何求 $lcm(i,j)$?** 提醒你: $$ lcm(i,j)=\frac{i*j}{gcd(i,j)} $$ 也就是说gcd求出来lcm也就求出来了。。。 [上题](https://www.luogu.org/problem/SP5971) #### 2 . exgcd: 用于求逆元 引理:**裴蜀定理**,一定存在$ax+by=gcd(a,b)$的整数解     乱证严格的证明: > ​ 当$b=0$时,$gcd(a,b)=a$,显然存在一对整数解$x=1,y=0$ > >     $gcd(10,0)=10$ > > ​ 若$b\neq0$ >       设$ax1+by1=gcd(a,b)=gcd(b,a \bmod b)=bx2+(a\bmod b)y2$ > >       又因 $a\bmod b=a-a/b*b$ > >       则 $ax1+by1=bx2+(a-a/b*b)y2$ > >       $ax1+by1=bx2+ay2-a/b*by2$ > >       $ax1+by1=ay2+bx2 -b* a/b * y2$ > >       $ax1+by1=ay2+b(x2-a/b*y2)$ > >       解得 $x1=y2 , y1=x2-a/b*y2$ > >       因为当 $b=0$ 时存在 $x , y$ 为最后一组解 > >       而每一组的解可根据后一组得到 > >       所以第一组的解 $x , y$ 必然存在 ~~你要是不懂那就背吧~~ 那么就到了激动人心的代码时间啦 求: $$ ax\equiv1\pmod{b} $$ $$ ax\bmod b =1 ->ax + by = 1 $$ 原理是,方程 $ax + by = m$有解的必要条件是 $m \bmod gcd(a,b) = 0$ 这个简单证一下。 由最大公因数的定义,可知 $a$ 是 $gcd(a,b)$ 的倍数,且 $b$ 是 $gcd(a,b)$ 的倍数, 若 $x,y$ 都是整数,就确定了 $ax + by$ 是 $gcd(a,b)$ 的倍数, 因为 $m = ax + by$,所以 $m$ 必须是 $gcd(a,b)$ 的倍数, 那么 $m \bmod gcd(a,b) = 0$ 蒟蒻版: ```cpp void exgcd (ll a, ll b, ll &x, ll &y) { if (b == 0) { x = 1; y = 0; return ; } exgcd (b, a % b, x, y); ll tmp = x;//#1此时的x=x2,y=y2,tmp = x2 x = y;//x 存储答案 y = tmp - a / b * y;//y1 = x2 - a/b*y2 //推上一个y1,从下往上推 } ``` 萌新版: ```cpp #include <bits/stdc++.h> #define int long long using namespace std; int x, y, a, b; void exgcd(int a, int b, int &x, int &y) { if(b) exgcd(b, a%b, y, x), y-=(a/b)*x;//压缩,不会自己thinkthink //敲黑板(重点)exgcd(y,x)不是(x,y) else x=1,y=0; } signed main() { scanf("%lld%lld",&a,&b); exgcd(a,b,x,y); printf("%lld",(x%b+b)%b);//x为逆元 return 0; } ``` ### 四、欧拉函数( 浅谈 ) #### **1、积性函数** ###### 概念 > 积性函数是对数论中一系列有特殊性质函数的统称,性质如下: > 若 > $$ gcd(a,b)=1, f(ab)=f(a)f(b) $$ > 那么 f 则是积性函数 > > 特别地,假如对于任意的a、b,即 > $$ f(ab)=f(a)f(b) $$ > 那么称 f 为完全积性函数 #### 2、欧拉函数概念 > 定义:欧拉函数$φ(n)$表示[1,n]范围内与n互质的数的个数。 > $$ \varphi( n )= \sum_{i=1}^{n} [ gcd ( i,n ) = 1 ] $$ #### **3、基本性质** ###### 1.对于一个质数$p$, $φ (n) = p - 1$ (相对来说用的比较多) 证明:根据质数的定义,对于任意一个不是$p$倍数的正整数$x$,总有$gcd(p,x)=1$,即$p$与$x$互质。在$1~p$之间,只有$1×p$是$p$的倍数,故在1~p中,与$p$互质的数为$1$到$p-1$,共$p-1$个,$\varphi(p)=p-1$。 ###### 2.对于一个质数p, $$ \varphi(n) = p^{k}-p^{k-1}=p^{k-1}*(p-1)=p^{k-1}*\varphi(p) $$ 证明:我们知道,在 1~p 之间与 p 不互质的数共1个,在 1~2×p 之间与 p 不互质的数共2个……在1~p^(k−1)×p 之间有 p^(k−1) 个。所以在 1~p^k 之间与 p 不互质的数有 p^(k−1) 个,互质的那就是p^(k)−p^(k−1),故φ(p^k)= p^k−p^(k−1),即第二个式子。然后提取公因数 p^(k−1) 得到第三个式子。结合性质1得到第四个式子。 ###### 3.对于一个数n,我们将其质因数分解为 $$ n=\prod_{i=1}^{k}p_{i}^{ri} $$ ###### 那么 $$ \varphi(n)=\prod_{i=1}^{k}\varphi(p_{i}^{ri})=\prod_{i=1}^{k}p^{ri-1}*(p-1)=n*\prod_{i=1}^{k}(1-\frac{1}{pi}) $$ **这个就是 _唯一分解定理_ 了** 证明:由于欧拉函数有积性,$φ(ab)=φ(a)φ(b)$,所以有 $$ \varphi(n)=\prod_{i=1}^{k}\varphi(p_{i}^{ri}) $$ 结合性质2的第三个式子,我们可以得到第三个式子。对于第三个式子,我们提取一个公因子p,得到 $$ \prod_{i=1}^{k}p_{i}^{ri}*(1-\frac{1}{pi})=(\prod_{i=1}^{k}p^{ri})*(\prod_{i=1}^{k}(1-\frac{1}{pi}))=n*\prod_{i=1}^{k}(1-\frac{1}{pi}) $$ 得证。 #### 4、扩展性质( 不证明 ) ###### 1.假如$a,b$不互质,那么$φ(ab)$等于什么呢? 结论:令$d=gcd(a,b)$,则 $$ \varphi(ab)=\frac{\varphi(a)\varphi(b)d}{\varphi(d)} $$ ###### 2.对于任意正整数$n$,$φ(n)$与$n$有什么样的关系? 结论: $$ n=\sum_{d|n}\varphi(d) $$ #### 5、欧拉函数求法 ① 直接求单独的phi值(只求某一个) 原理:质因子分解(基本性质3)。每次找到一个素因子,之后把它”除干净“,即可保证找的因子全部都为素数(这个比较容易理解,从2开始进行循环,删除二的所有倍数,因为含有倍数,则必定不是素数,从而确保下次循环所选的数必定为素数,额…若仍不理解,可以自己打表看一下,而事实也确实如此)。 ```cpp int phi(int n){ int ans=n,m=(int)sqrt(n+0.5);//细节问题,自己thinkthink for(int i=2;i<=m;i++){//筛选素因子 if(n%i==0){//i必为素数(为什么,自己thinkthink) ans=ans/i*(i-1); while(n%i==0) n/=i; //每次都将素数因子除干净,这样就保证了每次寻找的i均为素数。均在唯一式内 } } if(n>1) ans=ans/n*(n-1);//若n不为1,则说明还有素数未除,则是n本身,继续进行上面的操作 return ans; } ``` ② 线性筛 (咋又是它嘞) 由于欧拉函数有积性,所以结合扩展性质1: 令$d=gcd(a,b)$,则 $$ \varphi(ab)=\frac{\varphi(a)\varphi(b)d}{\varphi(d)} $$ 我们可以通过线性筛质数,在筛出合数 $x * pri[j]$ 的同时计算出 $φ( x*pri[j] )$。(注:$pri[j]$ 为已经筛出来的质数,见上面的线性筛 ) 不过,需要分2种情况计算: 1. **$x$ 与 $pri[j]$ 互质.** 这种情况比较简单。由于$d=1$,$φ(d)=1$,所以 $$ \varphi(x*pri_{j})=\varphi(x)*\varphi(pri_{j}) $$ 直接计算即可 2. **$x$ 与 $pri[j]$ 不互质.** 这种情况下,$x$ 只可能是 $pri[j]$ 的倍数,故$ d = gcd( x, pri[j] ) = pri[j]$,根据扩1,所以: $$ \varphi(x*pri_{j})=\frac{\varphi(x)\varphi(pri_{j})pri_{j}}{\varphi(pri_{j})}=\varphi(x)*pri_{j} $$ 带入计算即可 所以代码就出炉啦 ```cpp #include <bits/stdc++.h> #define int long long #define MAXN 3005000 using namespace std; int phi[MAXN];//储存φ值 int not_prime[MAXN]; int prime[MAXN]; int n, p, tot; void shake_it() { phi[1] = 1; for(int i = 2; i <= n; i++) { if(!not_prime[i]) { prime[++tot] = i; phi[i] = i - 1;//基本性质1 } for(int j = 1; prime[j]*i <= n; j++) { not_prime[prime[j]*i] = 1; if(i%prime[j]==0)//如果i是prime的倍数 { phi[prime[j]*i] = phi[i] * prime[j]; break; } phi[prime[j]*i] = phi[i] * (prime[j] - 1); } } } signed main() { scanf("%lld", &n); shake_it();//线性筛 return 0; } ``` #### 6、欧拉定理 ##### ① 概念 > 若正整数 a , n 互质,则 > $$ a^{\varphi(n)}\equiv1\pmod{n} $$ > 其中 $φ(n)$ 是欧拉函数(1~n) 与 n 互质的数 ~~证明太***就不证明了~~ ##### ② 费马小定理 > 对于质数 p,任意整数 a,均满足: > $$ a^{p}\equiv a\pmod{p} $$ > 即两边同时除 a 得: > $$ a^{p-1}\equiv1\pmod{p} $$ #### 7、扩展欧拉定理 当 $a , m ∈ Z$ 时有: $$ a^{b} = \left\{\begin{matrix} a^{b}& , & b<\varphi(m)\\ a^{b\,mod\,\varphi(m)+\varphi(m)}&, & b\geq\varphi(m) \end{matrix}\right.\pmod{m} $$ 证明略。 [P5091 【模板】欧拉定理]( https://www.luogu.org/problem/P5091 ) [P4139 上帝与集合的正确用法](https://www.luogu.org/problem/P4139) ### 五、逆元 #### 0、转自[Nickel_Angel's nest](https://www.luogu.org/blog/1239004072Angel/post-shuo-xue-sheng-fa-ni-yuan) #### 1、同余公式 > (a+b)%p = (a%p + b%p) %p > > (a-b)%p = (a%p - b%p) %p > > (a*b)%p = (a%p) * (b%p) %p 推论:a^b %p = (a%p)^b %p 但是……除法? (a/b)%p ≠ (a%p) / (b%p) %p 怎么办? ~~这个时候就要呼叫超级飞侠啦~~ #### 2、逆元定义 乘法逆元,一般用于求 $$ \frac{a}{b} \pmod p $$ 的值($p$ 通常为质数),是解决模意义下分数数值的必要手段。 ~~不知道定义都不知道后面在BB啥~~ > 若 > $$ a*x\equiv1 \pmod {b} $$ > 且a与b互质,那么我们就能定义:x 为 a 的逆元,记为 > $$ a ^{-1} $$ > > 所以我们也可以称 x 为 a 在 modb 意义下的倒数, > > 所以对于 > $$ \displaystyle\frac{a}{b} \pmod {p} $$ > 我们就可以求出 b 在 mop 下的逆元,然后乘上 a,再 modp,就是这个分数的值了。 #### 3、逆元求法(整理[原博客]( https://www.luogu.org/blog/1239004072Angel/post-shuo-xue-sheng-fa-ni-yuan ) ) ##### ① EXGCD求逆元 ​ 同上不讲。 时间复杂度为 $O(log\,\, p)$ ##### ② 欧拉定理求逆元 观察式子 $$ ax \equiv 1 \pmod{p} $$ 我们发现它和欧拉定理 $$ a^{\varphi(p)} \equiv 1 \pmod{p} $$ 很像…… 而欧拉定理又是 $a,p$ 互质时成立,这和逆元存在的条件相同…… 故将两个式子合在一起,得: $$ ax\equiv a^{\varphi(p)}\pmod{p} $$ 两边同时除 a 得: $$ x\equiv a^{\varphi(p)-1}\pmod{p} $$ 故 $$ x = a^{\varphi(p)-1}mod p $$ 求出欧拉函数后,可以使用快速幂求出答案。 code: ```cpp #include <cstdio> #include <iostream> #include <algorithm> using namespace std; long long n, p; long long phi(long long x)//求x的欧拉函数 { long long res = x, tmp = x;//初始化答案为x for (int i = 2; i * i <= tmp; ++i) { if (x % i == 0) { res = (res / i) % p * ((i - 1) % p) % p;//找到x的一个质因子,计算其对答案的贡献 while (x % i == 0) x /= i;//统计完答案后,除去该质因子 } } if (x > 1) res = (res / x) % p * ((x - 1) % p) % p;//防止漏筛质因子 return res; } long long power(long long a, long long b)//快速幂 { long long res = 1; while (b) { if (b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res; } int main() { scanf("%lld%lld", &n, &p); long long tmp = phi(p) - 1; printf("%lld", power(n, tmp)); return 0; } ``` 时间复杂度为: $$ O(log\varphi(p)+\sqrt p) $$ ##### ② 费马小定理求逆元 **TIP:** 仅能在 p 为质数的时候使用 > 费马小定理:若 p 为质数,且整数 a 满足 p | a ,则有: > $$ a^{p-1}\equiv1\pmod{p} > $$ 对于定理中的式子,我们可以将两边同除 a ,得: $$ a^{p-2}\equiv a^{-1}\pmod{p} $$ 而 a^(-1)就是 a在模 p 意义下的乘法逆元。 故我们只需计算 a^(p-2) mod p 即可,而这个过程可用快速幂解决。 我们不妨看看最初的问题,如何对一个分数取模? $$ x=\frac{a}{b}modp=ab^{-1}modp $$ 只需要算 b 的逆元就行啦 时间复杂度为 $O(logp)$ ##### ③ 线性求逆 **TIP:** 仅能在 p 为质数的时候使用 基于拓展欧几里得算法,我们虽然可以在 O(n log p)时间内,求出 [1,n]中所有整数在模质数 p 下的乘法逆元,但在面对更大的数据范围时,我们就需要更快的方法。 首先,我们不难发现,对于任何正整数 $c$,$1 * 1^{-1} ≡ 1 (mod c)$ ,即 1 在模任意正整数意义下的逆元为其本身。然后设 $p = ki + r(r < i, 1 < i < p)$,将其转化为同余式会得到: $$ ki+r\equiv0\pmod{p} $$ 两边同时乘 $i ^{-1}, r^{-1}$,得: $$ i^{-1}r^{-1}(ki+r)\equiv0\pmod{p} $$ 将式子乘开: $$ kr^{-1}+i^{-1}\equiv0\pmod{p} $$ 移项得: $$ i^{-1}\equiv -kr^{-1}\pmod{p} $$ 由于 $p = ki + r (r < i, 1 < i <p)$ 易知 $k = p/i$(下取整), $r = p \bmod i$,故有: $$ i^{-1}\equiv-\left \lfloor\frac{p}{i} \right \rfloor*(p\,mod\,i)^{-1}\pmod{p} $$ 而 $p \bmod i < i$ ,故 $p \bmod i$ 的逆元我们之前已经递推出来了,故我们可以通过这个式子推出 i 的逆元是谁。 最终式子为: $$ i^{-1}\equiv(p-\left \lfloor\frac{p}{i} \right \rfloor)*(p\,mod\,i)^{-1}\pmod{p} $$ code: ```cpp #include <bits/stdc++.h> #define int long long #define MAXN 5000000 using namespace std; int n, p; int x, y; int inv[MAXN]; signed main() { scanf("%lld%lld", &n, &p); inv[1] = 1; for(int i = 2; i <= n; i++) { inv[i] = (p-p/i)*inv[p%i]%p; printf("%d\n", inv[i]); } return 0; } ``` **TIP:** 当然还有很多的方法,这里只列举比较常用的几种求逆元的方法 [P2613 【模板】有理数取余](https://www.luogu.org/problem/P2613) [P5431 【模板】乘法逆元2](https://www.luogu.org/problem/P5431) ### 六、CRT/EXCRT #### 1.CRT ##### 0.转载某[大佬]( https://www.luogu.org/user/111987 )(%%%),蒟蒻修改部分 ##### 1.定义: 若m[1],m[2],...,m[n] 是两两互质的正整数, $M= \prod_{i=1}^n{m[i]}$,$M[i]=M/m[i] ,t[i]$ 是线性同余方程 $M[i] * t[i] ≡1(\bmod m[i])$的一个解。对于任意的$n$个整数$a[1], a[2], a[3], ... , a[n]$,则同余方程组为: $$ \left\{\begin{matrix} x\equiv a_{1}\pmod{m_{1}}\\ x\equiv a_{2}\pmod{m_{2}}\\ ......\\ x\equiv a_{n}\pmod{m_{n}} \end{matrix}\right. $$ 有整数解,方程组的解为 $$ x=a_{1}M_{1}t_{1}+a_{2}M_{2}t_{2}+...+a_{n}M_{n}t_{n} $$ 并且在%M意义下有唯一解。 ##### 2.证明: 因为$M[i] = M/m[i]$ 是除$m[i]$ 之外所有模数的倍数,所以 $∀$ (任意)$k \neq i$,$a[i]M[i]t[i]≡0(\bmod m[k])$.又因为 $a[i]M[i]t[i] ≡a[i] (mod m[i])$,所以代入$x$得 $$ x=\sum_{i=1}^{n}a_{i}M_{i}t_{i} $$ ##### 3.结论: 中国剩余定理给出了模数两两互质的线性同余方程组的一个特殊解。方程组的通解可以表示为$x+kM(k∈Z)$。有些题目要求我们求出最小的非负整数解,只需把x对M取模,并让x落在0~M-1的范围内即可。 ```cpp int intchina(int r) { int Mi,x,y,d,ans=0; int M=1;//注意取1 for(int i=1;i<=r;i++) M*=m[i]; for(int i=1;i<=r;i++){ Mi=M/m[i]; exgcd(Mi,m[i],d,x,y); ans=(ans+Mi*x*a[i])%M; //Mi相当于Mi,x—>ti } return (ans+M)%M;//防负数 } ``` [P1495 曹冲养猪](https://www.luogu.org/problem/P1495) #### 2.EXCRT 某位神仙说提高不会考,如果考到~~我认命~~,如果实在想学可以点击[这里](https://www.luogu.org/problemnew/solution/P4777) ### 七、线性代数 #### 0.前置 线性代数比较难(矩阵)而且不咋考,~~所以就马马虎虎地走个过场~~ #### 1.快速幂 这个大家都会,我就只贴板子了 如果你实在要搞懂原理 [戳我](https://www.luogu.org/problemnew/solution/P1226) 求: $$ a^{b}\,mod\,p $$ code: ```cpp ll quick_pow(ll a, ll b, ll p)//a^b mod p { ll t=1; while(b) { if(b&1) t=t*a%p; a=a*a%p; b>>=1; } return t; } ``` #### 2.矩阵相关 %%%大佬 **可爱即是正义** 直接使用传送术(懒得排了,也不是很重要的东西) [可爱即是正义 的博客](https://www.luogu.org/blog/shehuizhuyihao/post-zhen-sheng-fa) #### 3.*高斯消元 某位神级清华sang师说高斯消元noip不会考,不属于提高组,所以不讲,如果感兴趣自己下去查吧。 ### 八、组合数学 #### 0.前置: 这个也是考的比较多的 #### 1.卡特兰数(卡姿兰数) **0、说明:** ​ [原博客](https://blog.csdn.net/wookaikaiko/article/details/81105031) ##### 1.卡特兰数是啥: > 卡特兰数是一种经典的组合数,经常出现在各种计算中,其前几项为 : 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, 6564120420, 24466267020, 91482563640, 343059613650, 1289904147324, 4861946401452, ... TIP:Cn,hn,fn均表示卡特兰数(在这) ##### 2.卡特兰数的公式 ###### ① 递归公式1 $$ f(n)=\sum_{i=0}^{n-1}f(i)*f(n-i-1) $$ 如果我们用这个公式显然我们要使用递归算法,那么数据一大就在时空上很麻烦 ###### ② 递归公式2 $$ f(n)=\frac{f(n-1)*(4*n-2)}{(n+1)} $$ 这个公式应用递推,看上起十分和善 但对大数据呢? 我们注意到大数据的时候 f(n) 会很大,这时候题目一般会让你对某素数取模(~~当然你可以打高精度~~) 但你在取模过程中难保一个 f(n)%mod=0 那么根据公式下面所有的数都会等于0,于是你就愉快的WA了 ###### ③ 组合公式1 $$ f(n)=\frac{C_{2n}^{n}}{(n+1)} $$ 卡特兰数可以与组合数联系起来,得到上面的公式 而组合数就是一个杨辉三角,可以递推得到 但我们发现对于大数据你要取模,而对于除法你是没办法用膜的性质的(~~当然你可以应用逆元~~),所以造成了麻烦 ###### ④ 组合公式2 $$ f(n)=C_{2n}^{n}-C_{2n}^{n-1} $$ 与组合数公式1不同这个是两个组合数的减法 减法是可以用膜的性质的,于是你可以愉快的AC了。 ##### 3.卡特兰数的应用 **①、进出栈问题**: 栈是一种先进后出(FILO,First In Last Out)的数据结构.如下图1,2,3,4顺序进栈,那么一种可能的进出栈顺序是:1In→2In→2Out→3In→4In→4Out→3Out→1Out, 于是出栈序列为1,3,4,2。 ![](http://lanqi.org/wp-content/uploads/2015/11/QQ20151105-5-300x141.png) **那么一个足够大的栈的进栈序列为1,2,3,⋯,n时有多少个不同的出栈序列?** [例题]( https://www.luogu.org/problem/P1044 ) 我们可以这样想,假设 x 是最后一个出栈的数。 由于x是最后一个出栈的,所以可以将已经出栈的数分成两部分 ​ 1.比 x 小 ​ 2.比 x 大 比x小的数有x-1个,所以这些数的全部出栈方案数为f[x-1] 比x大的数有n-x个,所以这些数的全部出栈方案数为f[n-x] 所以一共有 $f[x-1]*f[n-x]$ 种方案。显而易见,$x$ 取不同值时,产生的出栈序列是相互独立的,所以结果可以累加。$x$的取值范围为 $1$ 至 $ n$,所以结果就为 $f[n] = f[0] * f[n-1] + f[1] * f[n-2] + ... + f[n-1] * f[0]$ **②. 二叉树构成问题** 有n个结点,问总共能构成几种不同的二叉树? (1)先考虑只有一个节点的情形,设此时的形态有 $f[1]$ 种,那么很明显 $f[1]=1$ (2)如果有两个节点呢?我们很自然想到,应该在 $f[1]$ 的基础上考虑递推关系。 那么,如果固定一个节点后,左右子树的分布情况为$1=1+0=0+1$,故有$f[2] = f[1] + f[1]$ (3)如果有三个节点,我们需要考虑固定两个节点的情况么?(当然不,因为当节点数量大于等于$2$时,无论你如何固定,其形态必然有多种 我们考虑固定一个节点,即根节点)。好的,按照这个思路,还剩2个节点,那么左右子树的分布情况为$2=2+0=1+1=0+2$。 所以有$3$个节点时,递归形式为 $f[3]=f[2]+f[1]*f[1]+f[2]$。(注意这里的乘法,因为左右子树一起组成整棵树,根据排列组合里面的乘法原理即可得出) (4)那么有n个节点呢?我们固定一个节点,那么左右子树的分布情况为 $n-1=n-1 + 0 = n-2 + 1 = … = 1 + n-2 = 0 + n-1$。此时递归表达式为$f[n] = f[n-1] + f[n-2]f[1] + f[n-3]f[2] + … + f[1]f[n-2] + f[n-1]$ 即给定$N$个结点,能构成$h(N)$种不同的二叉树。$h(N)$为卡特兰数的第$N$项。(二叉树性质) **③、凸多边形的三角形划分** 一个凸的$n$边形,用直线连接他的两个顶点使之分成多个三角形,每条直线不能相交,问一共有多少种划分方案。 这也是非常经典的一道题。我们可以这样来看,选择一个基边,显然这是多边形划分完之后某个三角形的一条边。图中我们假设基边是$p1-pn$,我们就可以用$p1,pn$和另外一个点假设为$pi$做一个三角形,并将多边形分成三部分,除了中间的三角形之外,一边是$i$边形,另一边是$n-i+1$边形。$i$的取值范围是$2$到$n-1$。所以本题的解$c(n)=c(2)c(n-1)+c(3)c(n-2)+...c(n-1)c(2)$。令$t(i)=c(i+2)$。则$t(i)=t(0)t(i-1)+t(1)t(i-2)...+t(i-1)t(0)$。很明显,这就是一个卡特兰数了。 ![](https://p-blog.csdn.net/images/p_blog_csdn_net/dlyme/%E4%B8%89%E8%A7%92%E5%88%92%E5%88%862.jpg) **④、求n+1个叶子的满二叉树的个数** ![QQ20151105-6](http://lanqi.org/wp-content/uploads/2015/11/QQ20151105-6.png) 不证,知道就行 **⑤、路径问题 ** 在n*n的格子中,只在下三角行走,每次横或竖走一格,有多少中走法? 其实向右走相当于进栈,向左走相当于出栈,本质就是n个数出栈次序的问题,所以答案就是卡特兰数。(利用这个模型,我们解决这个卡特兰问题的变形问题,并顺便给进出栈问题的解法一个几何解释) ![神奇的卡特兰数](http://www.cppblog.com/images/cppblog_com/miyu/450px-Catalan_number_4x4_grid_example.svg.png) **⑥、划分问题** 将一个凸n+2边形区域分成三角形区域的方法数? ![神奇的卡特兰数](http://www.cppblog.com/images/cppblog_com/miyu/400px-Catalan-Hexagons-example.svg.png) ~~证明都差不多啦,自己研究去吧~~ **⑦、握手问题** 2n 个人均匀坐在一个圆桌边上,某个时刻所有人同时与另一个人握手,要求手之间不能交叉,求共有多少种握手方法 ##### 4、引理 这就不说了,没啥对考试太实用的,如果实在想看就往上翻,去原博客看,考试知道这些就行了 ##### 5、code 说了一堆终于到终点了哈 代码来自 [xiejinhao](https://www.luogu.org/user/196649) (%%%) ①公式1: ```cpp #include<cstdio> #define MAX_N 20//这你自己看要定义多大吧 #define ll long long using namespace std; int n; ll f[MAX_N]; int main() { f[0]=f[1]=1; scanf("%d",&n); for(int i=2;i<=n;i++) { for(int j=0;j<i;j++) { f[i]+=f[j]*f[i-j-1]; } } printf("%lld",f[n]); return 0; } ``` ②公式2: ```cpp #include<cstdio> #define MAX_N 20 #define ll long long using namespace std; int n; ll f[MAX_N]; int main() { f[0]=f[1]=1; scanf("%d",&n); for(int i=2;i<=n;i++) { f[i]+=f[i-1]*(4*i-2)/(i+1); } printf("%lld",f[n]); return 0; } ``` ③公式3: ```cpp #include<cstdio> #define MAX_N 20 #define ll long long using namespace std; int n; ll c[MAX_N*2][MAX_N]; int main(){ scanf("%d",&n); for(int i=1;i<=2*n;i++) { c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) { c[i][j]=c[i-1][j]+c[i-1][j-1]; } } printf("%lld",c[2*n][n]/(n+1)); return 0; } ``` ④公式4: ```cpp #include<cstdio> #define MAX_N 20 #define ll long long using namespace std; int n; ll c[MAX_N*2][MAX_N]; int main(){ scanf("%d",&n); for(int i=1;i<=2*n;i++) { c[i][0]=c[i][i]=1; for(int j=1;j<i;j++) { c[i][j]=c[i-1][j]+c[i-1][j-1]; } } printf("%lld",c[2*n][n]-c[2*n][n-1]); return 0; } ``` ~~不懂看公式去~~ 卡姿兰数完结 #### 2、排列组合 ##### 1、计数原理: 如果 A 地到 B 地有三条路径,B 地到 C 地有两条路径,那么A 到 C 共有 2 * 3 = 6 种方案 如果 A 还有一种直接到 C 的方案,那么就一共有 7 种方案 上面就是加法原理和乘法原理(不知道自行[百度](https://baike.baidu.com/item/%E8%AE%A1%E6%95%B0%E5%8E%9F%E7%90%86/4032370?fr=aladdin),~~好像不知道也无所谓哦~~) ##### 2、排列和组合: 从 n 个元素中取出 m 个元素,排成一排,叫做从 n 个元素中取出 m 个元素的一个排列 $$ A_{n}^{m}=\frac{n!}{(n-m)!} $$ 从 n 个元素中取出 m 个元素的所有组合(不管其顺序)的个数,叫做 n 个元素中取出 m 个元素的组合数 $$ C_{n}^{m}=\frac{n!}{m!(n-m)!} $$ 这东西太多了,我们讲几个基础的 $$ C_{n}^{m}=C_{n}^{n-m},C_{n}^{0}=C_{n}^{n}=1 $$ 可以理解为:将原本的每个组合都反转,把原来没选的选上,原来选了的去掉,这样就变成从n个元素种取出n−m个元素,显然方案数是相等的。 $$ C_{n}^{m}=C_{n-1}^{m}+C_{n-1}^{m-1} $$ 可理解为:含特定元素的组合有$C^{m-1}_{n-1}$,不含特定元素的排列为$C^{m}_{n-1}$ 组合数求和公式: $$ C_{n}^{0}+C_{n}^{1}+C_{n}^{2}+...+C_{n}^{n}=2^{n} $$ ~~自己记去~~ #### 3、二项式定理 可以将x+y的任意次幂展开成和的形式 $$ (x+y)^{n}=\binom{n}{0}x^{n}y^{0}+\binom{n}{1}x^{n-1}y^{1}+\binom{n}{2}x^{n-2}y^{2}+...+\binom{n}{n-1}x^{1}y^{n-1}+\binom{n}{n}x^{0}y^{n} $$ 其中每个$\binom{n}{k}$为一个称作二项式系数的特定正整数,其等于$\frac{n!}{k!(n-k)!}$。这个公式也称二项式公式或二项恒等式。使用求和符号,可以把它写作 $$ (x+y)^{n}=\sum_{k=0}^{n}\binom{n}{k}x^{n-k}y^{k}=\sum_{k=0}^{n}\binom{n}{k}x^{k}y^{n-k} $$ [P1313 计算系数](https://www.luogu.org/problem/P1313) #### 4、杨辉三角 来自古人的神秘力量 [大佬博客](https://www.cnblogs.com/1024th/p/10623541.html)的搬运工——ollie ![](https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=8388214a7f8b4710da22f59ea2a7a898/a1ec08fa513d269760c7d13658fbb2fb4316d841.jpg) 杨辉三角可以帮助你更好地理解和记忆组合数的性质: 1. 第$n$行的$m$个数可表示为 $C^{m-1}_{n-1}$,即为从$n-1$个不同元素中取$m-1$个元素的组合数。 2. 第$n$行的数字有$n$项。 3. 每行数字左右对称(第$n$行的第$m$个数和第$n-m+1$个数相等,$C_{n}^{m}=C_{n}^{n-m}$,由$1$开始逐渐变大。 4. 每个数等于它上方两数之和(第$n+1$行的第$i$个数等于第$n$行的第$i-1$个数和第$i$个数之和,即$C_{n+1}^{i}=C^{i}_{n}+C_{n}^{i-1}$。 1. $(a+b)^{n}$的展开式中的各项系数依次对应杨辉三角的第$n+1$行中的每一项(二项式定理)。 **code:** ```cpp c[1][1] = 1; for(int i = 2; i <= n; i++) for(int j = 1; j <= i; j++) { c[i][j] = c[i-1][j] + c[i-1][j-1]; } ``` [P1118 数字三角形](https://www.luogu.org/problem/P1118) [P2822 组合数问题](https://www.luogu.org/problem/P2822) #### 5、Fibonacci数列 都知道吧,什么是Fibonacci数列。 递推式: $$ F(1)=1,F[0]=0 $$ $$ F(n)=F(n-1)+F(n-2) $$ 通项公式: $$ F(n)=\frac{1}{\sqrt{5}}[(\frac{1+\sqrt{5}}{2})^{n}-(\frac{1-\sqrt{5}}{2})^{n}] $$ 关于斐波那契的一些恒等式: 1:$F(1)+F(2)+F(3)+...+F(n)=F(2n-1)$ 2:$F(1)^{2}+F(2)^{2}+F(3)^{2}+...+F(n)^{2}=F(n)F(n+1)$ 3:$F(1)+F(3)+F(5)+...+F(2n-1)=F(2n)$ 4:$F(2)+F(4)+F(6)+...+F(2n)=F(2n+1)-1$ 5:$F(n)=F(m)F(n-m+1)+F(m-1)F(n-m)$ $ps:n>m$ 6:$F(n-1)F(n+1)=F(n)^{2}+(-1)^{n}$ **斐波那契的数论相关:** **性质1**:$gcd(F(n),F(m))=F(gcd(n,m))$ 反证法:假设不互素。那么有$a=gcd(F(n),F(n-1)),a>1$. 那么对于$F(n)=F(n-1)+F(n-2)$.因为$a|F(n),a|F(n-1)$,所以$a|F(n-2)$. 由于$a|F(n-1),a|F(n-2)$.又可以获得$a|F(n-3)$...可以知道$a|F(1)$其中。$F(1)=1$. 如果$a|F(1)->a|1$那么与$a>1$不符。相邻互素得证.(其实 $a|F(2)$就已经不行了.) **性质2:** $n|m\Leftrightarrow F(n)|F(m)$ 证明:当$n|m$时, $$ gcd(F(n),F(m))=F(gcd(n,m))=F(n)\Rightarrow F(n)|F(m) $$ [P1306 斐波那契公约数](https://www.luogu.org/problem/P1306) ### 九、数学期望 所有的权值乘以它对应的概率 通俗一点就是设$X[i]$为$i$的权值,$P[i]$是$i$对应出现的概率,期望值为$f$,有: $$ f=\sum_{i=1}^{n}X[i]* P[i] $$ ## 完结撒花 ### 还有部分内容未补上,后期补充上去