五一数学
Day 1
一、矩阵
就是 行 列的二维数组,用中括号框起来。
例如当 时,有一个矩阵 如下:
矩阵加减
将对应位置的两个元素相加,比较容易理解。
减法同理。
矩阵乘数
也是比较简单,用所有元素乘上这个数。
矩阵乘矩阵
定义 是 行 列, 是 行 列。
设答案矩阵为 ,那么 一定是 行 列。
具体来看做法:
可以有技巧的算:,说起来比较绕,但是懂得了规律以后会发现十分简单。
这里给出一张图片比较好理解:
咕咕
代码
矩阵乘法:
点击查看代码
struct Matrix { int n,m;//n:矩阵行数 m:矩阵列数。 int a[105][105];//矩阵 Matrix ()//构造函数,作用:初始化矩阵为 0。 { n = m = 0; memset(a,0,sizeof (a)); } }; Matrix operator * (const Matrix &a,const Matrix &b) { //重载 * 运算,起作用当且仅当是 Matrix 类型。 //&:取地址符,操作的时候是直接修改两个矩阵。 //const:防止运算的时候把运算的矩阵本尊破坏。 //注意类型。 Matrix ans;//返回的答案 int x = a.n,y = b.m; int i,j,k; for(i=1;i<=x;i++) for(j=1;j<=y;j++) //枚举每一个位置。 for(k=1;k<=a.m;k++) //核心部分 ans.a[i][j] += a.a[i][k] * b.a[k][j]; return ans; }
Day 2
一、模运算
逆元的定义
我们都知道模运算有分配率,但是只适用于乘法,除法不能运用分配率,要想求出除法的模运算,需要做出一些转化。
我们想要求出 ,但是硬算是求不出来的,需要做一些转化。
我们需要一个整数 ,使得 。
Tips: 等同于
我们就称 为 的逆元。
求逆元的方法
费马小定理
如果 为质数且 ,那么有 。两边同时除以 ,得 。
所以 的逆元是 ,可以用快速幂计算。
欧拉定理
条件:,如果 ,逆元不存在。
此时有 。
Tips: 表示从 到 中有多少个数与 互质。
那么 的逆元是 。
怎么计算?有两种算法。
1.暴力计算,复杂度 .
2.规律:。
点击查看代码
int get_phi(int n) { int phi = n; int i; for(i=2;i*i<=n;i++) { if(n%i==0) { phi = phi/i*(i-1); while(n%i==0)//把质因子噶掉 n = n/i; } } if(n!=1) phi = phi/n*(n-1); return phi; }
我们设 。
根据小学学过的 ,可以得出 。
转换成 。
把 移到右边,得 。
两边同时除以 ,得出 ,也就是 。
继续移项,得出 。
C++
语句为:inv[i] = 1ll*(p-k)%p*inv[r]%p
。
其中 inv[i]
为 。
代码:
点击查看代码
#include<bits/stdc++.h> const int N = 1e6; typedef long long ll; using namespace std; ll inv[N],n,p;//inv 数组表示 1/i。 int main() { cin>>n>>p; inv[1] = 1; int i; for(i=2;i<=n;i++) { ll k = p/i,r = p%i; inv[i] = 1ll*(p-k)%p*inv[r]%p; } for(i=1;i<=n;i++) printf("%lld\n",inv[i]); return 0; }
exgcd
给定 ,求任意一组整数解 ,满足 。
我们令 ,因为 ,所以把 和 也写成类似的形式:
想要推出 ,考虑把 拆开,得出:
把括号拆开,得:
把带有 的凑到一起,带 的凑在一起,得:
此时,我们就发现这个式子与原来的式子一样了。所以:
此时递归求出 的解即可。
终止条件?当 时,,所以让 即可。
可以用 维护三个元素 ,也可以顺便求出 了。
代码:
点击查看代码
struct pi { int x,y,d; pii(int _x=0,int _y=0,int _d=0):x(_x),y(_y),d(_d){} }; pi exgcd(int a,int b) { if(!b) return {1,0,a}; pi GCD = exgcd(b,a%b); return {GCD.y,GCD.x-a/b*GCD.y,GCD.d}; }
裴蜀定理
现在有一个问题: 都是整数,那么 的最小正整数,这说明了,只有答案是 的倍数时,方程才有解,这就是裴蜀定理。
现在要求 ,满足 是 的倍数,怎么做?
- 把得到的 同时乘 即可。
现在虽然求出了一组解,但是想要求出通解改怎么办?
- 设 是一开始 求出的一组解,那么通解可以写成 的形式,其中 是任意整数。
例题 1 - 同余方程
既然这道题有解,那么 。
-
做法一:可以发现 是 的逆元。因为 不一定是质数,所以用欧拉定理解即可。
-
做法二:改写一下他的形式。既然 ,那么 一定可以写成 的形式,把他移项,变成 ,那么直接 即可。
但是题目要求得是最小正整数解,考虑通项公式。容易发现 。
由于 ,所以 ,容易发现答案就是 ,所以 (...%mod+mod)%mod
即可。
二、素数 & 质数
判断一个数是否是质数
如何判断一个数为质数?可以从 暴力枚举到 ,判断 n%i==0
即可。注意最大因子不会超过 ,所以可以从 枚举到 ,复杂度 。
代码:
点击查看代码
bool is_prime(int n) { if(n<=1) return 0; if(n==2) return 1; if(n%2==0) return 0; int i; for(i=3;i*i<=n;i++)//不要用 sqrt(n),复杂度爆表。 { if(n%i==0) return 0; } return 1; }
但如果 加强到 ,上述方法就通过不了,此时需要优化。
运用 进行素性测试。
素数往往满足一下两条定理的至少一条。
如果 是质数,那么有 ,存在一个 ,那么
-
-
如果 ,那么有
但是有些合数往往也满足上述定理之一,我们就要随机取一些 ,增加素数成功的概率。
Tips:上述定理中,素数可能会满足一条或者两条,但一定会满足一条,但是有些合数可能不会满足,也可能会满足一条或者两条,需要进一步排查。如果满足了两条,成为素数的概率不会大于满足一条的概率。
质数筛
- 埃氏筛
点击查看代码
const int N = 1e6; int not_prime[N],prime[N]; int cnt; void make_era() { int i,j; for(i=2;i<=n;i++) if(not_prime[i]) for(j=i*2;j<=n;j+=i) not_prime[i]=1; for(i=1;i<=n;i++) if(!not_prime[i]) prime[++cnt]=i; }
复杂度大约为 。
- 欧拉筛
点击查看代码
const int N = 1e6; int not_prime[N],prime[N]; int cnt; void make_ola() { int i,j; for(i=2;i<=n;i++) { if(!not_prime[i]) prime[++cnt] = i; for(j=1;j<=cnt;j++) { int x = prime[j]*i; if(x>n) break; not_prime[x]=1; if(i%prime[j]==0) break; } } }
复杂度为 。
Day 3
一、BSGS 算法
有一个式子,,给定 ,求 。
暴力
我们可以知道循环节长度为 。这是因为费马小定理中, 是质数,时间复杂度为 。
点击查看代码
int baoli (int a, int b, int p) { ll v = 1; int x; for(x=0;x<p-1;x++) { if(v==b) return x; else v = 1ll*v*a%p; } return -1; }
正解
因为循环节为 ,所以只需要枚举前 个数。
将这 个数分组,设每组 个数,我们可以得出许多组,像下面一样。
我们就可以从第一组查找满足条件的 ,如果没有找到,就从第二组找……这还是一种暴力的思想。
我们将两组联系起来,发现第二组每一个数都是第一组的 倍,将第二组的每个数处以 ,就得出了第一组。
如果第二组存在 ,那么第一组一定存在 。
如果第 组出现 ,那么第一组一定存在 。
可以运用哈希或者二分查找。
代码:
点击查看代码
set<ll> se; int BSGS(int a,int b,int p) { int s = sqrt(p); ll v = 1; int i,j; for(i=0;i<s;i++) { se.insert(v); v = 1ll*v*a%p; } for(i=0;i*s<=p;i++)//看看答案是否在第 i 行里面 { //要看 b*a^(-is) 是否在第零行出现 ll c = 1ll*b*ksm(ksm(a,i*s,p),p-2,p); if(se.count(c)!=0) { ll v = ksm(a,i*s,p); for(j=i*s;;j++) { if(v==b) return j; v = 1ll*v*a%p; } } } return -1; }
懒人最爱的 set
!!
复杂度为
二、排列组合
排列
三个人中选两个人站成一排,考虑顺序。
有 种情况:
公式:
组合
三个人中选两个人站成一排,不考虑顺序。
有 种情况:
公式:
组合数有以下性质:
显然 个数中选 个数的方案为 。
选 个数也等于不选 个,当然推式子也可以算出。
到 个物品中有两种选择:选或不选,所以为 。
详细讲一下第四个式子。
考虑第一个数选或不选,即两种可能。
如果选择了,那么要从剩下的 个中选 个;
如果没有选择,那么要从剩下的 个中选 个,加起来就是 个中选 个,即 。
这里给出递推式代码:
点击查看代码
int C[1001][1001]; ll p; void make_C(int n,int m)//用递推式做 { int i,j; for(i=0;i<=n;i++) { C[i][0] = 1; for(j=1;j<=i;j++) C[i][j] = (C[i-1][j-1]+C[i-1][j])%p; } return; }
转化成图像怎么解释呢?通过画图,得出是一个杨辉三角。
-
当 时,有
-
把 展开 次,得:
例题 1
有 到 的 个数,每个数可以选很多次,从中选出 个数,不计顺序,求方案数。
先考虑只能选一次的情况。将这些书排序,其实就是求不等式 的解的数量。答案为 。
那么如何把 干掉呢?令 ,那么原式就变成了 。此时的解为 ,把 还原成 ,所以本题的答案就是 。
例题 2
给出 ,求 ,其中 。
用分类讨论思想解决。
-
一、如果 ,答案是 。
-
二、如果 ,那么可以用递推式来算。
-
三、如果 , 是质数,那么怎么做呢?
用逆元+阶乘解决。
给出代码:
点击查看代码
int fac[1000001]; int find_C(int n,int m,int p) { fac[0] = 1; int i; for(i=1;i<=1000000;i++) fac[i] = 1ll*fac[i-1]*i%p; return 1ll*fac[n]*ksm(fac[m],p-2,p)%p*ksm(fac[n-m],p-2,p)%p; }
- 四、如果 ,。
挺小的,所以突破口就在 上。猜测一下复杂度约为 。
我们进行约分,得:
Tips: 在 C++
中表示为:for(i=1;i<=n;i++) sum *= i
。
把乘法拆开,分子与分母的项数都为 左右。
因为答案是整数,所以把分子与分母约分,直到分母全部为 为止。把分子乘起来就是答案。约分要用到 ,所以复杂度为 。
点击查看代码
int fenmu[10001],fenzi[10001]; int find_C(int n,int m,int p) { int i,j; for(i=1;i<=m;i++) fenmu[i] = i,fenzi[i] = n-i+1; for(i=1;i<=m;i++) { for(j=1;j<=m;j++) { int g = gcd(fenzi[i],fenmu[j]); fenzi[i] /= g; fenmu[j] /= g; } } int ans = 1; for(i=1;i<=m;i++) ans = 1ll*ans*fenzi[i]%p; return ans; }
- 五、如果 , 且是质数。
用 定理做。
定理的定义:
解释成人话就是:将 转换成 进制数, 等于 在 进制下的各位相乘。
设 拆成 进制位 , 拆成 ,那么 。
其中 与 都是一位数字。
通过发现 比较小,所以 可以直接 推出来,剩下的继续递归。
终止条件: 时,返回 。
举个例子:当
那么有
此时的做法为:暴力预处理出组合数,然后用短除法将 分解成 进制,然后诸位计算即可。
复杂度 。
代码;
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long int c[101][101]; int x[10001]; int y[10001]; int Lucas(int n,int m,int p) { //n,m<=10^9 //p <=100 且是质数 int i; while(n!=0) { x[0]++; x[x[0]] = n%p; n /= p; } while(m!=0) { y[0]++; y[y[0]] = m%p; m /= p; } //x[1] x[2]....x[x[0]] 是 n 的 p 进制从低到高的表示 //y[1] y[2]....y[y[0]] 是 m 的 p 进制从低到高的表示 int ans = 1; for(i=1;i<=x[0];i++) ans = 1ll*ans*c[x[i]][y[i]]%p; return ans%p; } signed main() { int n,m,p; scanf("%d%d%d",&n,&m,&p); int i,j; for(i=0;i<=p;i++) { c[i][0] = 1; for(j=1;j<=i;j++) c[i][j] = (c[i-1][j-1]+c[i-1][j])%p; } printf("%d\n",Lucas(n,m,p)); return 0; }
如果要求 ,怎么办?
将 质因数分解,,分别计算 的结果,分别设为 ,那么有
直接 即可。
例题 3
洛谷 P4369
给你两个数 和 ,请给出 个不同的组合数,使得它们的和相加为 。
所谓不同,即对于 和 的组合数, 且
,那么就称这两个组合数是不同的。
诈骗题。
众所周知,一个正整数 可以拆成 个 相加,既然要求 个组合数,那我们就让前 个组合数为 ,最后一个为 ,就可以让和为 了。
那么前 个组合数是什么呢?我们知道 ,那就让这些组合数的 “” 都为 ,这样就可以通过了。
点击查看代码
#include<bits/stdc++.h> using namespace std; int main() { int n,k; cin>>n>>k; int i; for(i=1;i<k;i++) printf("%d 0\n",i); printf("%d 1\n",k); return 0; }
例题 4
给出 ,比较 和 的大小。
首先来讲一下 。
如果 ,那么 。
有 ,。
Tips:这里的 都是同底的,同底不影响大小。
如果 ,那么 。
根据上述式子,可以算出 。
首先
所以
然后就可以计算了。想要求出 的阶乘,可以从 考虑,用递推算即可。
点击查看代码
int faclog[1000001]; double logger(int n,int m) { faclog[0] = 0; int i; for(i=1;i<=100000;i++) faclog[i] = faclog[i-1]+log(i); return faclog[n]-faclog[m]-faclog[n-m];//比较 log(C(n1,m1)) 和 log(C(n2,m2)) }
例题 5
给出 ,让你找到 个不同的组合数,使得这 个组合数的和最大。要求这些组合数 满足 ,求出这个最大的和。。
先把杨辉三角搞出来:
我们发现在三角中,最后一行的中间往往比较大,我们就可以考虑从中间优先选择。
那么第二大的数呢?肯定实在最大的上下左右。我们可以比较一下那个数更大。
可以向四周扩充几个点,维护一个候选集,从里面挑一个最大的,并且重新更新,重复 次即可。
就是一个裸的 。
例题 6
咕咕
三、抽屉原理
定义: 个物品放入 个抽屉里,至少有一个抽屉有多余 个物品。
推广:把 个物品放在 个抽屉里,必然有一个抽屉有 个物品。
例题 1
给定 个数,随便选择任意个数,使得它们的和是 的倍数,任意给出一组解。。
引入前缀和。
如何求出 到 的和?是 ,即 。
有了前缀和后,该怎么解题呢?
发现前缀和是连续的一段和,那我们就让题目复杂一点,即必须选连续的一些数。
把每个前缀和对 取模,如果 ,那么这一段就是答案,否则放到 个抽屉里面,那么肯定有两个前缀和放在一个抽屉里面,那么多出来的和就一定是 的倍数。
例题 2
洛谷 P2218
平面上有 个点,每个点有坐标 ,用 个 的正方形覆盖所有点(平行于坐标轴),求最小的 。。
我们先随便定一个 ,例如 。
假设现在已经覆盖住了所有点,那么 的时候,可以覆盖住吗?或者 的时候,可以全部覆盖吗?
答案分别是可以、不可以。
我们可以先找到边缘的四个点,即左、右、上、下。然后画一个大正方形,根据抽屉原理,一定会有一个正方形盖住两个点,所以这两个点就会成为正方形的顶点。
于是我们可以暴力枚举盖住哪个角,暴力枚举三次,最后检验一下每个点是否都被覆盖即可。
四、容斥原理
定义:有 个学语文的人, 个学语文的人。
那么 表示同时学语文和数学的人, 表示学语文或学数学的人。
可以得出 。
画个图好理解:咕咕。
此时又出现了学英语的人,。
有
此时又双叒叕出现了打游戏的人,。
可以根据上面的式子的出来,但是比较复杂,懒得列出来了()
骗你的我这么勤奋,当然会列出来呢。
快给我点赞点赞点赞!!
可以总结出 个种类的人时,容斥原理的公式。
例题 1 - 对夫妻问题
奇怪的名字。
对夫妻,一共 个人。把他们排成一圈,满足每对夫妻都不相邻且旋转后相同的方案算一种,求方案数。
先考虑随坐的方案数,为 。
我们先强制让一对夫妻相邻,即 。
把那一对夫妻绑在一起,看成一个人,此时就剩下了 个人,方案数为 。
但是我们可以把一夫一妻调换位置,所以要 。
整理一下,让一对夫妻相邻的方案数为 。
此时算完了吗?并没有,因为有算重的情况。
例题 2
给出 ,询问 到 中有多少个数可以表示成 的形式。。
Day 4
一、解 方 程
是的你没看错,这一章讲的就是解方程。
二元一次方程
先给出一组二元一次方程:
用 式减去 式,得 ,可以算出 ,随便带入一个式子,得 。
多元一次方程
以此类推,可以算出三元、四元等方程组中。
给出一个多元一次方程组:
怎么解呢?可以用高斯消元来解决。
高斯消元的基本逻辑:每一次消掉一个未知数,减少一个方程,最后得到一个一元一次方程来求解的方法。
先给一个例子:
把他们对齐,并补齐系数(没有字母则为 )。
成为:
可以抽象看成一个矩阵乘法:
把等式的右边提到左边,得:
接下来开始高斯消元:
先把第一行同时乘上一个数,此时第二行的每个元素都加上第一行的对应的数乘上这个倍数。
这个倍数该怎么选择呢?通常情况下,第二行第一列的这个数,需要加上第一行第一列的数乘上这个倍数,使得第二行第一列的数为 。
我们来进行第一次高斯消元:
可以看到如果倍数选了 ,此时第二行第一列的数为 ,第二行第二列的数为 ,第二行第三列的数为 ,第二行第四列的数为 。
留坑待补。
二、扩展矩阵
如果有两个 的矩阵,他们两个相乘,为 矩阵,如果 矩阵的对角线为 ,其他数为 ,那么就称 矩阵为单位矩阵。
我们就把 和 互相称为逆矩阵。
是 的逆矩阵, 也是 的逆矩阵。
和 有以下性质:
留坑待补。
三、概率
引入一个例子:扔一个骰子,每一面朝上的概率为多少呢?
因为骰子有 个面,每个面上都有一个数字,所以这些概率相等,都为 。
把 这几个数称为样本空间,其中任意一个数称为样本点。事件是这些样本点的集合,即样本点相加为事件。
先来熟悉一下集合:
两个集合,。
,即 与 共有的部分。
,即 与 中出现过的数。
,即在 中 没有出现过的数。
把 称为事件 发生的概率。注意每个样本点的概率不一定相等,一个事件的概率就是事件内样本点的概率的和。
概率有一些性质:
-
。
-
假设样本空间有 个样本点,,那么 。
-
如果 ,那么把 称为必然事件;若 ,称 为不可能事件。
把 称为在 发生的情况下,发生 的概率,这就是条件概率。
举个例子:当 。
当 。就是 个中满足 个概率的条件。
条件概率有一个简单的公式:
给一张图片加以解释:
如果 发生了,就是红色的部分,如果 同时发生,就是蓝色部分。所以就可以得出了上述公式。
同时乘上 ,也可以得出 。
如果 事件发生,而 事件不发生,那么把这两个事件称为独立事件。
若 与 是独立事件,那么:
与条件概率相结合,可以得出:
那么
四、期望
继续扔骰子,扔到每个数的概率为 ,求每个数的期望。
啥是期望?也就是每个事件的权值 事件的概率并求和,那么扔骰子的概率为:
那么求扔出的数的平方的期望呢?
为:
期望也有一些性质:
- 期望的和 和的期望。例如有两个骰子:一个是正常的,每个数的概率都是 ;第二个骰子被 施了魔法,扔出 的概率是 ,其他 个数字都是 ,那么有:
换成平方也可以:
例题 1
有 张形状相同的卡片,一张两面都是黑色,一张两面都是红色,一张一面红一面黑。随机取出一张放在桌子上,朝上的为红色,那么另一面为黑色的概率是多少?
根据条件概率的计算,可得:
例题 2
个人按照一个顺序依次抓阄,每个人抓到了以后立即打开,当有人抓中后,游戏结束(只有一个“中”)。问游戏是否公平,并说明理由。
公平。理由如下:
第一个人抽到的概率为 。
第二个人抽到的概率为 。
第三个人抽到的概率为 。
以此类推,可以得出概率都是 ,所以公平。
例题 3
设男女人口比例为 ,男人色盲概率为 ,女人色盲的概率为 ,现随机抽到了一个人为色盲,请问该人为男人的概率是多少。
条件概率式子为:
懒得算了。
例题 4
一个人左右口袋里面各有一盒火柴,每盒 支。每次抽烟时随机选一盒拿出一只并用掉,由于习惯原因,选右面口袋的概率是 。问:下述两种概率是否相等?并求出概率的值。
-
到某次他发现取出的这一盒已经空了,这时另一盒剩下 支。
-
他用完某一盒时,另一盒恰好有 支火柴。
我们可以列出左口袋与有口袋摸的概率:左边为 ,右边为 。
如果有 个火柴,发现取完了以后,此时取了 次。
留坑待补。
例题 5
小葱和小泽面前有三瓶药,其中两瓶是毒药,一瓶是果汁,每个人必须喝一瓶。
小葱和小泽各自选择了一瓶药,小泽手速比较快,把药喝了下去,然后就挂掉了。
小葱想要活下去,他看到手上的药陷入了沉思——他应该喝掉手上的这一瓶还是换成剩下的一瓶呢?
两个人选择的药一共有 种情况,运用条件概率可以得出以下式子:
所以换或者不换,无所谓。
例题 6
上一个问题的扩展——“三门问题”。
三个门,一个里面有车,剩下的两个有 叫的羊,现在你选择了一个门,主持人知道哪个门有车、哪个门有羊。
留坑待补。
例题 7
小葱想要过河,过河有两条路:
-
一条路有 个石头,每个石头有 的概率会挂掉。
-
一条路有 个石头,每个石头有 的概率会挂掉。
小葱应该走哪条路呢?
这个问题相对比较简单。
第一条路存活的概率:。
第二条路存活的概率:
可以用计算器计算。
那么如果在数学考试中,不让用计算器呢?那该怎么做呢?
先把指数约分一部分,就成为了: 和 。
我们都知道 。
所以 。
所以 。
所以答案是不过河走第二条路。
例题 8
小胡站在原点,手里拿着两枚硬币。抛第一枚硬币正面向上的概率为 ,第二枚正面向上的概率为 。
- 阶段一:小胡开始抛第一枚硬币,每次抛到反面小胡就向 轴正方向走一步,直到抛到正面。
- 阶段二:接下来小胡继续抛第一枚硬币,每次抛到反面小胡就向 轴正方向走一步,直到抛到正面。
- 阶段三:现在小胡想回来了,于是他开始抛第二枚硬币,如果小胡抛到正面小胡就向 轴的负方向走一步,否则小胡就向 轴的负方向走一步。
现在小胡想知道他在往回走的时候经过原点的概率是多少呢?
分析:留坑待补。
式子:
其他
鸣谢
由于本人对关于膜运算的掌握较差,于是大部分参考的是 SYZ 大佬的博客
同时在更好的讲解时,参照了 TZF 大佬的博客 与 ZHZ 大佬的博客。
同时感谢 QZH 大佬的博客给了这篇博客创作的动力( 大佬写了 多行/bx/bx/bx)。
还有 CZH 大佬的支持 %%%%%%%%%%%%%%%。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步