2025.3.4数学听课笔记
容斥原理
子集容斥:
N个条件,已知(可以快速求出)不满足某些条件的人数。通过容斥原理求出满足所有条件的人数。
Ans=总人数-不满足条件1-不满足条件2-……+不满足条件一也不满足条件二+……-不满足条件一二三。
数学式子如下:
\[\sum_{i=1}^{s} (-1)^s \times \text {count}(s)
\]
证明略。
矩阵乘法
加法,乘法
加法:对应位置相加即可。
乘法:
\[C{_i}{_j}=\sum_{t=1}^{m}A{_i}{_,}{_t}\times B_{t,j}
\]
方便记忆:
将矩阵 \(A\) 看作一个 \(n\) 行的向量,\(B\) 看作 \(k\) 列的向量,最后得出的矩阵就是 \(a_i · b_j\)
具有结合律,分配律。无交换律
矩阵加速线性递推
如斐波那契数列,要求\(f_n\)。
可得\([f_{n-1},f_n]=[f_0,f_1]\times
\begin{bmatrix}
1&1\\
1&0
\end{bmatrix}
^{(n-1)}\)
重点在于找到转移的矩阵。
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int p=1e9+7;
const int N=5;
int n,k;
struct node{
long long a[N][N];
node(){memset(a,0,sizeof(a));};
};
node a,ans;
void init(node &x){
for(int i=1;i<=n;i++){
x.a[i][i]=1;
}
return;
}
node operator * (const node x,const node y){
node res;
memset(res.a,0,sizeof(res.a));
for(int k=1;k<=2;k++){
for(int i=1;i<=2;i++){
for(int j=1;j<=2;j++){
res.a[i][j]=(res.a[i][j]+x.a[i][k]*y.a[k][j]%p)%p;
}
}
}
return res;
}
void qpow(node a,int b){
ans.a[1][1]=1;ans.a[1][2]=1;
while(b){
if(b&1) ans=ans*a;
a=a*a;
b>>=1;
}
return ;
}
signed main(){
cin>>n;
if(n<=2){
cout<<1;
return 0;
}
a.a[1][1]=1,a.a[1][2]=1;
a.a[2][1]=1,a.a[2][2]=0;
qpow(a,n-1);
cout<<ans.a[1][2]%p;
return 0;
}
矩阵快速幂
单位矩阵\(\text I=\begin{bmatrix}
1&0\\
0&1
\end{bmatrix}\)
直接快速幂就可以了
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
const int p=1e9+7;
const int N=110;
int n,k;
struct node{
long long a[N][N];
};
node a;
void init(node &x){
for(int i=1;i<=n;i++){
x.a[i][i]=1;
}
return;
}
node operator * (const node x,const node y){
node res;
memset(res.a,0,sizeof(res.a));
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
res.a[i][j]=(res.a[i][j]+x.a[i][k]*y.a[k][j]%p)%p;
}
}
}
return res;
}
node ans;
void qpow(node a,int b){
init(ans);
while(b){
if(b&1) ans=ans*a;
a=a*a;
b>>=1;
}
return ;
}
signed main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a.a[i][j];
}
}
qpow(a,k);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<ans.a[i][j]<<' ';
}
cout<<'\n';
}
return 0;
}
高斯消元
N 元一次方程组求解
其实就是加减消元,最后变成一元一次方程,求出解后再慢慢带回原式。
如果最后的方程无解,会出现\(0≠0\)。
如果最后的方程多解,会出现\(0=0\)。
球形空间产生器
推一下式子,然后直接做就行。
#include<iostream>
#include<cmath>
#define eps 1e-7
using namespace std;
int n;
double c[12][12];
double a[12][12];
double ans[12];
void gj(){
int p;
for(int i=1,w=1;i<=n && w<=n;i++,w++){
p=w;
for(int j=w+1;j<=n;j++){
if(fabs(a[j][i])>fabs(a[p][i])) p=j;
}
if(p!=w) swap(a[w],a[p]);
for(int j=1;j<=n;j++){
if(w!=j){
for(int k=n+1;k>=w;k--){
a[j][k]-=a[j][i]/a[w][i]*a[w][k];
}
}
}
}
for(int i=1;i<=n;i++){
ans[i]=a[i][n+1]/a[i][i];
}
}
int main(){
cin>>n;
for(int i=1;i<=n+1;i++){
for(int j=1;j<=n;j++){
cin>>c[i][j];
}
}
for(int i=1;i<=n+1;i++){
for(int j=1;j<=n;j++){
a[i][j]=2*(c[i][j]-c[i+1][j]);
a[i][n+1]+=c[i][j]*c[i][j]-c[i+1][j]*c[i+1][j];
}
}
gj();
for(int i=1;i<=n;i++){
printf("%0.3lf ",ans[i]);
}
return 0;
}
线性基
在异或背景下,求异或和,会有多少种可能的异或值。
线性基三大性质
1. 原序列里面的任意一个数都可以由线性基里面的一些数异或得到。
2. 线性基里面的任意一些数异或起来都不能得到0。
3. 线性基里面的数的个数唯一,并且在保持性质一的前提下,数的个数是最少的。
cin>>n;
for(int i=1;i<=n;i++){
long long x;
cin>>x;
for(long long j=51;j>=0;j--){
if(x & (long long)1 << j){
if(!d[j]){
d[j]=x;
break;
}else{
x^=d[j];
}
}
}
}
long long ans=0;
for(int j=51;j>=0;j--){
if((ans^d[j])>ans) ans^=d[j];
}cin>>n;
for(int i=1;i<=n;i++){
long long x;
cin>>x;
for(long long j=51;j>=0;j--){
if(x & (long long)1 << j){
if(!d[j]){
d[j]=x;
break;
}else{
x^=d[j];
}
}
}
}
long long ans=0;
for(int j=51;j>=0;j--){
if((ans^d[j])>ans) ans^=d[j];
}
斯特林数
第一类:有 n 个数,分成 m 个互不区分的非空轮换的方案数。
第二类:有 n 个数,分成 m 个互不区分的非空子集的方案数。
递推公式:
\[S_1[n][m]=(n-1)\times s_1[n-1][m]+s_1[n-1][m-1]
\]
\[S_2[n][m]=S_2[n-1][m-1]+m\times S_2[n-1][m]
\]
卡特兰数
形式化描述:
- 从原点开始向右上或右下走,走到 \((2n,0)\) 的方案数。
- 有 \(2n\) 长的括号序列的合法序列方案数。
\[H_n=
\begin{pmatrix}
2n\\
n
\end{pmatrix}
-
\begin{pmatrix}
2n\\
n-1
\end{pmatrix}
\]