数学知识板子
文章目录
1.质数
试除法判定质数
分解质因数
筛质数
模板:
//质数的判定 —试除法
bool Is_prime(int n){
if(n<2) return false;
for(int i=2;i<=n/i;i++)
if(n%i==0)
return false;
return true;
}
//分解质因数 —试除法
void divide(int n){
// 性质:n中最多只包含一个大于 sqrt(n) 的质因子
for(int i=2;i<=n/i;i++)
if(n%i==0){ // i 一定是质数
int s=0;
while(n%i==0){
n/=i;
s++;
}
printf("%d %d",i,s);
}
if(n>1) printf("%d %d\n",n,i);
puts("");
}
//筛质数个数 --埃氏筛法
void get_primes(int n){
for(int i=2;i<=n;i++) //从2开始每次留下的数就是质数
if(!st[i]){
primes[cnt++]=n;
for(int j=i+i;j<=n;j+=i) st[j]=true;
//每次都只把质数的倍数删掉即可
}
}
//---线性筛法求个数 :n只会被最小质因子筛掉
void get_prime(int n){
for(int i=2;i<=n;i++){
if(!st[i]) prime[cnt++]=i;
for(int j=0;prime[j]<=n/i;j++){
st[prime[j]*i]=true;
if(i%prime[j]==0) break; //核心只被最小质因子筛一次--->线性 -->所以避免了"重复筛"
//i%pirme[j]==0 pj一定是i的最小质因子.pj一定是pj*i的最小质因子 break;
//i%prime[j]!=0 pj一定小于i的所有质因子,pj也一定是pj*i的最小质因子
}
}
}
2.约数
试除法求约数
输入样例:
2
6
8
输出样例:
1 2 3 6
1 2 4 8
模板:
//试除法求所有约数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> get_divisors(int n){
vector<int> res;
for(int i=1;i<=n/i;i++)
if(n%i==0){
res.push_back(i);
if(i!=n/i) res.push_back(n/i);
}
sort(res.begin(),res.end());
return res;
}
int main(){
int n;
cin>>n;
while(n--){
int x;
cin>>x;
auto res=get_divisors(x);
for(auto t : res) cout<<t<<" ";
cout<<endl;
}
return 0;
}
约数个数
输入样例:
3
2
6
8
输出样例:
12
模板:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<unordered_map>
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
int main(){
int n;
cin>>n;
unordered_map<int,int> primes;
while(n--){
int x;
cin>>x;
for(int i=2;i<=x/i;i++) //一直除质因数 2 3 5 7 11 ... 最多室友一个大于 sqrt(n) 的质因子
while(x%i==0){
x/=i;
primes[i]++;
}
if(x>1) primes[x]++; //大于sqrt(n) 的质因子和 可能本身是质因子的可能 ++
}
LL res=1;
for(auto prime : primes) res=res * (prime.second+1)%mod;
cout<<res<<endl;
return 0;
}
约数之和
输入样例:
3
2
6
8
输出样例:
252
模板:
//约数之和
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<unordered_map>
using namespace std;
const int mod = 1e9+9;
typedef long long LL;
int main(){
int n;
cin>>n;
unordered_map<int,int>primes;
while(n--){
int x;
cin>>x;
for(int i=2;i<=x/i;i++){
while(x%i==0){
x/=i;
primes[i]++;
}
}
if(x>1) primes[x]++;
}
LL res=1;
for(auto prime : primes){
int p=prime.first,a=prime.second;
LL t=1;
while(a--) t=(t*p+1)%mod; //------>数学
res=res*t%mod;
}
cout<<res<<endl;
return 0;
}
最大公约数(欧几里得算法)
输入样例:
2
3 6
4 6
输出样例:
3
2
模板:
//最大公约数
//欧几里得算法 (a,b)=(b,a mod b)
#include<iostream>
using namespace std;
int gcd(int a,int b){
return b ? gcd(b,a%b) : a;
}
int main(){
int n;
scanf("%d",&n);
while(n--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",gcd(a,b));
}
return 0;
}
3.欧拉函数
欧拉函数
输入样例:
3
3
6
8
输出样例:
2
2
4
模板:
//欧拉函数
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
int a;
cin>>a;
int res=a;
for(int i=2;i<=a/i;i++)
if(a%i==0)
{
res=res/i*(i-1);
while(a%i==0) a/=i;
}
if(a>1) res=res/a*(a-1);
cout<<res<<endl;
return 0;
}
筛法求欧拉函数
思路:
线性筛法求欧拉函数
1.当 i 为质数时: i 的欧拉函数为 i - 1
2.当 i % pj == 0 ,pj仅仅只是在N种增加了质因数的指数,在欧拉公式可以看出φ(i * pj) = pj * φ(i)。
3.当 i % pj != 0 ,pj在N中增加了一项质因数, 欧拉函数同加一项得φ(i * pj) = φ(i) * pj * (1 - 1/pj) 化简后得 φ(i * pj) = φ(i) * (pj - 1)
输入样例:
6
输出样例:
12
模板:
//线性筛法求欧拉函数
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int primes[N],cnt;
int phi[N];
bool st[N];
LL get_eulers(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!st[i]){
primes[cnt++]=i;
phi[i]=i-1; //本身是质数 那么互质的个数是 i-1
}
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i] =true;
if(i%primes[j]==0){ //只是指数增加不影响结果
phi[primes[j]*i]=phi[i]*primes[j];
break;
}
// primes[j]中不包含 i 的质因数
phi[primes[j]*i]=phi[i]*(primes[j]-1);
}
}
LL res=0;
for(int i=1;i<=n;i++) res+=phi[i];
return res;
}
int main(){
int n;
cin>>n;
cout<<get_eulers(n)<<endl;
return 0;
}
4.快速幂
快速幂
输入样例:
2
3 2 5
4 3 9
输出样例:
4
1
模板:
//快速幂
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1 % p;
while (b)
{
if (b & 1) res = res * a % p;
a = a * (LL)a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
int a, b, p;
scanf("%d%d%d", &a, &b, &p);
//a ^ b % p = ((a % p)^b) % p
printf("%lld\n", qmi(a % p, b, p));
}
return 0;
}
快速幂求逆元
思路:
逆元同余方程: a/b ≡ a * x( mod p) -> b * x ≡ 1(mod p) (即 x 为所求得逆元)
费马小定理: b^(p-1) ≡ 1(mod p) -> b * b ^(p-2) ≡ 1(modp) (当p为质数)
即当 p 为 质数时 x = b^(p-2)为所求得逆元 -> 快速幂
b 和 p成倍数一定无解
样例输入
3
4 3
8 5
6 3
样例输出:
1
2
impossible
代码模板:
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
LL qmi(int a, int b, int p)
{
LL res = 1;
while (b)
{
if (b & 1) res = res * a % p;
a = a * (LL)a % p;
b >>= 1;
}
return res;
}
int main()
{
int n;
scanf("%d", &n);
while (n -- )
{
int a, p;
scanf("%d%d", &a, &p);
if (a % p == 0) puts("impossible"); //p = 2 特判
else printf("%lld\n", qmi(a, p - 2, p));
}
return 0;
}
5.扩展欧几里得算法
扩展欧几里得算法
输入样例:
2
4 6
8 18
输出样例:
1 1
2 1
模板:
//裴蜀定理
//扩展欧几里得算法
#include<iostream>
#include<algorithm>
using namespace std;
//递推---
//推导过程
//ax0+by0=gcd(a,b)=gcd(b,a%b)=d(最大公约数) ax0 + by0
//gcd(b,a%b)=by1+(a%b)x1= by1+(a-|a/b|*b)x1= ax1 + b(y1-a/b*x1)=d
//x0=x1 y0=y1-a/b*x1
int exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,y,x);
y-=a/b*x; //<---ax + b(y-a/b*x)=d
cout<<"a="<<a<<" "<<"b="<<b<<endl;
cout<<"x="<<x<<" "<<"y="<<y<<endl;
return d; //这里返回得是最大公约数
}
int main(){
int n;
scanf("%d",&n);
while(n--){
int a,b,x,y;
scanf("%d %d",&a,&b);
exgcd(a,b,x,y);
printf("%d %d\n",x,y);
}
return 0;
}
线性同余方程
6.中国剩余定理
表达整数的奇怪方式
输入样例:
2
8 7
11 9
输出样例:
31
模板:
7.高斯消元
高斯消元解线性方程组
输入样例:
3
1.00 2.00 -1.00 -6.00
2.00 1.00 -3.00 -9.00
-1.00 -1.00 2.00 7.00
输出样例:
1.00
-2.00
3.00
模板:
//高斯消元解线性方程组
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 110;
const double eps = -1e-6;
int n;
double a[N][N];
void out(){
for(int i=0;i<n;i++){
for(int j=0;j<=n;j++) printf("%10.2lf",a[i][j]);
puts("");
}
puts("");
}
int gauss(){
int c,r; //c表示列 r表示行
for(int c=0,r=0;c<n;c++){
int t=r;
for(int i=r;i<n;i++)
if(fabs(a[i][c])>fabs(a[t][c])) //找到最大得 c
t=i;
if(fabs(a[t][c])<eps) continue;
for(int i=c;i<=n;i++) swap(a[t][i],a[r][i]); //换到最上面
for(int i=n;i>=c;i--) a[r][i]/=a[r][c]; //将该行第一个数变成 1
for(int i=r+1;i<n;i++) //将下面所有行得第 c 变成 0
if(fabs(a[i][c])>eps)
for(int j=n;j>=c;j--)
a[i][j]-=a[r][j]*a[i][c];
r++;
}
if(r<n)
{
for(int i=r;i<n;i++)
if(fabs(a[i][n])>eps)
return 2;//无解
return 1;//有无穷多组解
}
for(int i=n-1;i>=0;i--) //倒出唯一解
for(int j=i+1;j<n;j++)
a[i][n]-=a[i][j]*a[j][n];
return 0;//有唯一解
}
int main(){
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n+1;j++)
cin>>a[i][j];
int t=gauss();
if(t==0){
for(int i=0;i<n;i++) printf("%.2lf\n",a[i][n]);
}
else if(t==1) puts("Infinite group solution");
else puts("No solution");
return 0;
}
//fabs()对 double 取绝对值
高斯消元解异或线性方程组
8.求组合数
求组合数I
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
模板:
//求组合数I
//递推式 : C(a)\(b) = C(a-1)\(b) + C(a-1)\(b-1)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 2010,mod = 1e9+7;
int c[N][N];
void init(){
for(int i=0;i<N;i++)
for(int j=0;j<=i;j++)
if(!j) c[i][j]=1;
else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
int main(){
init();
int n;
scanf("%d",&n);
while(n--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",c[a][b]);
}
return 0;
}
求组合数II
思路
定义出发
C a b = a ! ( b − a ) ! b ! C_{a}^{b}=\frac{a!}{(b-a)!\ b!} Cab=(b−a)! b!a!
输入样例:
3
3 1
5 3
2 2
输出样例:
3
10
1
模板:
//求组合数II
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100010,mod = 1e9+7;
int fact[N],infact[N]; //阶乘
int qmi(int a,int k,int p){
int res=1;
while(k){
if(k&1) res=(LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
int main(){
fact[0]=infact[0]=1;
for(int i=1;i<N;i++){
fact[i]=(LL)fact[i-1]*i%mod;
infact[i]=(LL)infact[i-1]*qmi(i,mod-2,mod)%mod;
}
int n;
scanf("%d",&n);
while(n--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",(LL)fact[a]*infact[b]%mod*infact[a-b]%mod);
}
return 0;
}
求组合数III
思路:
卢卡斯定理: C m n ≡ C m / p n / p ∗ C m m o d p n m o d p ( m o d p ) C_{m}^{n}≡C_{m/p}^{n/p} * C_{m\ mod\ p}^{n\ mod\ p} (mod \ p) Cmn≡Cm/pn/p∗Cm mod pn mod p(mod p)
输入样例:
3
5 3 7
3 1 5
6 4 13
输出样例:
3 3 2
模板:
//求组合数III
//卢卡斯定理
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int p;
int qmi(int a,int k){ //快速幂求乘法逆元
int res=1;
while(k){
if(k&1)res = (LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
int C(int a,int b){ //阶乘
int res=1;
for(int i=1,j=a;i<=b;i++,j--){
res=(LL)res*j%p;
res=(LL)res*qmi(i,p-2)%p;
}
return res;
}
int lucas(LL a,LL b){ //卢卡斯定理
if(a<p && b<p) return C(a,b);
return (LL)C(a%p,b%p)*lucas(a/p,b/p)%p;
}
int main(){
int n;
cin>>n;
while(n--){
LL a,b;
cin>>a>>b>>p;
cout<<lucas(a,b)<<endl;
}
return 0;
}
满足条件的01序列
思路
卡特兰数 f ( n ) = C 2 n n − C 2 n n − 1 = C 2 n n n + 1 f(n) =C_{2n}^{n} - C_{2n}^{n-1} = \frac{C_{2n}^{n}}{n + 1} f(n)=C2nn−C2nn−1=n+1C2nn
输入样例:
3
输出样例:
5
模板:
//满足条件的 01 序列
//卡特兰数
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int mod = 1e9 +7;
int qmi(int a,int k,int p){
int res=1;
while(k){
if(k&1) res=(LL)res*a%p;
a=(LL)a*a%p;
k>>=1;
}
return res;
}
int main(){
int n;
cin>>n;
int a=2*n,b=n;
int res=1;
//定义出发
for(int i=a;i>a-b;i--) res=(LL)res*i%mod;
for(int i=1;i<=b;i++) res=(LL)res*qmi(i,mod-2,mod)%mod;
res=(LL)res*qmi(n+1,mod-2,mod)%mod;
cout<<res<<endl;
return 0;
}
9.容斥原理
能被整除的数
输入样例:
10 2
2 3
输出样例:
7
模板:
//容斥原理
//能被整除的数
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100010;
int n,m;
int p[N];
int main(){
cin>>n>>m;
for(int i=0;i<m;i++) cin>>p[i];
int res=0;
for(int i=1;i<1<<m;i++){ // 枚举所有集合的情况可以用位运算解决
int t=1,cnt=0; //t 为所有质数的乘积 cnt 当前选法里面有几个集合
for(int j=0;j<m;j++)
if(i>>j&1){
cnt++;
if((LL)t*p[j]>n){
t=-1;
break;
}
//cout<<"P[j]="<<p[j]<<" ";
t*=p[j];
}
//cout<<endl;
if(t!=-1){
if(cnt%2) res+=n/t;
else res-=n/t;
}
}
cout<<res<<endl;
return 0;
}
10.博弈论
NIM游戏
输入样例:
2
2 3
输出样例:
Yes
模板:
//NIM游戏
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n;
int res=0;
scanf("%d",&n);
while(n--){
int x;
scanf("%d",&x);
res^=x;
//cout<<"res="<<res<<endl;
}
if(res) puts("Yes");
else puts("No");
return 0;
}