介绍
1.素数
定义:对于一个正整数,如果它有且仅有 1 1 1 和它自己两个约数,那么我们称这个数为素数。如果有两个以上的约数,那么我们称这个数为合数。
唯一因子分解定理(质因数分解):合数能被唯一的分解为质数的乘积形式:
a = p 1 k 1 p 2 k 2 . . . p r k r , p 1 < p 2 < . . . < p r a=p_1^{k_1}p_2^{k_2}...p_r^{k_r},p_1<p_2<...<p_r a=p1k1p2k2...prkr,p1<p2<...<pr。
如何求素数
- 暴力枚举: O ( n ) O(\sqrt n) O(n)。
- 筛法求素数:用每个素数去筛掉它的倍数,时间 O ( n l o g n ) O(nlogn) O(nlogn)。
- 线性筛素数:在筛法的基础上 我们让每一个合数只能被他自己最小的素数筛到。
直接看代码:
for(int i = 2;i <= n;i++)
{
if(!isp[i])p[++cnt] = i;
for(int j = 1;j <= cnt&&i*p[j] <= n;j++)
{
isp[i*p[j]] = true;
if(i%p[j]==0) break;
}
}
2.gcd相关
定义: gcd ( a , b ) \gcd(a,b) gcd(a,b) 表示 a a a 和 b b b 的最大公约数, lcm ( a , b ) \operatorname{lcm}(a,b) lcm(a,b) 表示 a a a 和 b b b 的最小公倍数。
求法
知道这两个公式就行了: gcd ( a , b ) = g c d ( a − b , b ) \gcd(a,b)=gcd(a-b,b) gcd(a,b)=gcd(a−b,b), lcm ( a , b ) = a b gcd ( a , b ) \operatorname{lcm}(a,b)=\frac{ab}{\gcd(a,b)} lcm(a,b)=gcd(a,b)ab。
代码:
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int lcm(int x,int y){return x/gcd(x,y)*y;}
裴蜀定理
P4549 【模板】裴蜀定理
裴蜀定理:设
d
=
gcd
(
a
,
b
)
d=\gcd(a,b)
d=gcd(a,b),则d为
{
a
x
+
b
y
}
\{ax+by\}
{ax+by}(
x
,
y
∈
Z
x,y\in Z
x,y∈Z)中的最小正数(
d
d
d 的倍数均在集合中且集合中的数都是
d
d
d 的倍数)
一个重要的推论是:
a
x
+
b
y
=
1
ax+by=1
ax+by=1 有解等价于
a
a
a 和
b
b
b 互质。
扩展欧几里得
欧几里得算法只能告诉我们
a
x
+
b
y
=
m
ax+by=m
ax+by=m 这个方程有没有解(
gcd
(
a
,
b
)
∣
m
\gcd(a,b)\mid m
gcd(a,b)∣m 成不成立),而不能说明解是多少,使用扩展欧几里得算法可以解决这个问题。
a
x
+
b
y
=
d
ax+by=d
ax+by=d
b
′
x
+
(
a
%
b
)
y
′
=
d
b'x+(a\%b)y'=d
b′x+(a%b)y′=d
欧几里得算法是一个递归的过程,那我们也用递归来求解
x
x
x 和
y
y
y,现在已知
x
’
x’
x’ 和
y
’
y’
y’,求
x
x
x 和
y
y
y。
变个形:
a
%
b
=
a
−
⌊
a
b
⌋
×
b
a\%b=a-\lfloor\frac{a}{b}\rfloor\times b
a%b=a−⌊ba⌋×b
然后把含
a
a
a 的和含
b
b
b 的分别凑一块:
a
y
′
+
b
(
x
′
−
⌊
a
b
⌋
×
y
′
)
=
d
)
ay'+b(x'-\lfloor\frac{a}{b}\rfloor\times y')=d)
ay′+b(x′−⌊ba⌋×y′)=d) 就是递推关系了。
代码:
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x = 1;y = 0;return;}
exgcd(b,a%b,x,y);ll t = x;
x = y;y = t-a/b*y;
}
逆元
对于一个数
x
x
x,它在模
p
p
p 意义下的逆元
a
a
a,满足
a
x
≡
1
(
m
o
d
p
)
ax\equiv 1\pmod p
ax≡1(modp)。
如何求逆元?
- 扩展欧几里得,这式子不是和 a x + b y = m ax+by=m ax+by=m 长得很像吗?直接做就完了,时间复杂度: O ( l o g max ( a , b ) ) O(log_{\max(a,b)}) O(logmax(a,b))。
- 费马小定理:若 p p p 为素数,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1\pmod p ap−1≡1(modp),所以 a p − 2 a^{p-2} ap−2 就是 a a a 的逆元,用快速幂就行了。
- 线性求逆元:
将 p p p 写成 k × i + r k\times i+r k×i+r 的形式,其中 0 < r < i 0<r<i 0<r<i(即带余除法)。
k × i + r ≡ 0 ( m o d p ) k\times i+r\equiv 0\pmod p k×i+r≡0(modp)
两边同时除以 r × i r\times i r×i 得: k × r − 1 + i − 1 ≡ 0 ( m o d p ) k\times r^{-1}+i^{-1}\equiv 0\pmod p k×r−1+i−1≡0(modp)
所以 i − 1 ≡ − k × r − 1 ( m o d p ) i^{-1}\equiv-k\times r^{-1}\pmod p i−1≡−k×r−1(modp)
代码就是:
inv[0] = inv[1] = 1;
for(int i = 2;i <= n;i++)inv[i] = inv[mod%i]*(mod-mod/i)%mod;
P3811 【模板】乘法逆元
P2613 【模板】有理数取余
中国剩余定理(CRT)
P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪
和拉格朗日插值有点像,只需要构造出一个
x
x
x 就行了。
设
M
=
∏
i
=
1
k
m
i
M=\prod_{i=1}^km_i
M=∏i=1kmi,
M
i
=
M
m
i
M_i=\frac M{m_i}
Mi=miM,
t
i
t_i
ti 为
M
i
M_i
Mi 的逆元,则
x
=
∑
i
=
1
n
a
i
M
i
t
i
x=\sum_{i=1}^na_iM_it_i
x=∑i=1naiMiti,那么最小解就是
x
%
M
x\%M
x%M。
证明很简单,你把每个式子带进去算一下就行了。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
int n,a[11],b[11];
ull sum=1,t[11],ans;
int main()
{
cin>>n;
for(int i=1; i<=n; i++) {
cin>>a[i]>>b[i];
sum*=a[i];
}
for(int i=1; i<=n; i++) t[i]=sum/a[i];
for(int i=1; i<=n; i++)
for(ull j=t[i]; j<=0x7f7f7f7f7f7f; j+=t[i])
if(j%a[i]==1) {ans+=j*b[i]%sum;break;}
cout<<ans%sum;
return 0;
}
扩展中国剩余定理(exCRT)
P4777 【模板】扩展中国剩余定理(EXCRT)
和普通
C
R
T
CRT
CRT 一样有一组同于方程组,只不过模数不一定是质数。
思路也不一样了,是两个同余方程先合并,得到一个
x
x
x 的解,再依次合并下去。
{
x
≡
b
1
(
m
o
d
a
1
)
x
≡
b
2
(
m
o
d
a
2
)
.
.
.
x
≡
b
n
(
m
o
d
a
n
)
\left\{\begin{matrix} x\equiv b_1\pmod{a_1}\\ x\equiv b_2\pmod{a_2}\\ ...\\ x\equiv b_n\pmod{a_n} \end{matrix}\right.
⎩
⎨
⎧x≡b1(moda1)x≡b2(moda2)...x≡bn(modan)
代码:
#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
const int N = 1e5+5;
int n;
ll s[N],yu[N],ans;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b){x = 1;y = 0;return a;}
ll g = exgcd(b,a%b,x,y),tp = x;
x = y;y = tp-a/b*y;
return g;
}
ll CRT()
{
ll yu1 = yu[1],s1 = s[1];
for(int i = 2;i <= n;i++)
{
ll yu2 = yu[i],s2 = s[i];
ll d = yu2-yu1,g,x,y;
g = exgcd(s1,s2,x,y);
if(d%g)return -1;
ll now = s2/g;
x = (x*(d/g)%now+now)%now;
yu1 = x*s1+yu1;s1 = s1/g*s2;
}
return yu1;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
scanf("%lld%lld",s+i,yu+i);
printf("%lld\n",CRT());
return 0;
}
3.组合数学
定义:
从
n
n
n 个互不相同的物品里面选出
m
m
m 个排成一列(有先后顺序),总共有多少种选法?
这是一个排列问题,我们将这个方案数定义为
P
(
n
,
m
)
P(n,m)
P(n,m)。
第一次有n种取法,第二次有
n
−
1
n-1
n−1 种…第
m
m
m 次有
n
−
m
+
1
n-m+1
n−m+1 种,根据乘法原理,方案数应该为
n
!
(
n
−
m
)
!
\frac{n!}{(n-m)!}
(n−m)!n!。即
P
(
n
,
m
)
=
n
!
(
n
−
m
)
!
P(n,m)=\frac{n!}{(n-m)!}
P(n,m)=(n−m)!n!。
那如果没有先后顺序呢?这个方案数就是
C
(
n
,
m
)
C(n,m)
C(n,m),也可以记作
(
m
n
)
\binom{m}{n}
(nm)。
可以发现组合数相对排列数,就只是把顺序去掉了,即
(
1
,
2
)
(1,2)
(1,2) 和
(
2
,
1
)
(2,1)
(2,1) 是同一种选法,把
m
m
m 个无序的物品排成有序的有
m
!
m!
m! 种方法,所以
C
(
n
,
m
)
=
n
!
(
n
−
m
)
!
m
!
C(n,m)=\frac{n!}{(n-m)!m!}
C(n,m)=(n−m)!m!n!。
关于组合数的性质:
- C ( n , 0 ) = 1 C(n,0)=1 C(n,0)=1,不选也是一种方案。
- C ( n , m ) = 0 ( n < m ) C(n,m)=0\ (n<m) C(n,m)=0 (n<m)。
- C ( n , m ) = C ( n , n − m ) C(n,m)=C(n,n-m) C(n,m)=C(n,n−m),从 n n n 个里面拿出 m m m 个和留下 n − m n-m n−m 个等价。
- C ( n , m ) = C ( n − 1 , m − 1 ) + C ( n − 1 , m ) C(n,m)=C(n-1,m-1)+C(n-1,m) C(n,m)=C(n−1,m−1)+C(n−1,m),可以由这条性质 O ( n 2 ) O(n^2) O(n2) 求出杨辉三角。
- 可以根据第 4 4 4 条性质推出: ∑ i = m n ( m i ) = ( m + 1 n + 1 ) \sum_{i=m}^{n}\binom{m}{i}=\binom{m+1}{n+1} ∑i=mn(im)=(n+1m+1)。
二项式定理
即求
(
x
+
y
)
n
(x+y)^n
(x+y)n 拆开后各项的系数。
可以单独考虑
x
m
y
n
−
m
x^my^{n-m}
xmyn−m,就是从
n
n
n 个
(
x
+
y
)
(x+y)
(x+y) 中选出
m
m
m 个
x
x
x,即
C
(
n
,
m
)
C(n,m)
C(n,m)。
所以
(
x
+
y
)
n
=
∑
i
=
0
n
(
i
n
)
x
i
y
n
−
i
(x+y)^n=\sum_{i=0}^n\binom{i}{n}x^iy^{n-i}
(x+y)n=∑i=0n(ni)xiyn−i。
应用:
0
n
=
(
1
+
(
−
1
)
)
n
=
C
(
n
,
0
)
−
C
(
n
,
1
)
+
C
(
n
,
2
)
.
.
.
+
(
−
1
)
n
C
(
n
,
n
)
0^n=(1+(-1))^n=C(n,0)-C(n,1)+C(n,2)...+(-1)^nC(n,n)
0n=(1+(−1))n=C(n,0)−C(n,1)+C(n,2)...+(−1)nC(n,n)
即奇数项之和等于偶数项之和。
2
n
=
(
1
+
1
)
n
=
C
(
n
,
0
)
+
C
(
n
,
1
)
+
.
.
.
+
C
(
n
,
n
)
2^n=(1+1)^n=C(n,0)+C(n,1)+...+C(n,n)
2n=(1+1)n=C(n,0)+C(n,1)+...+C(n,n)
即所有项之和为
2
n
2^n
2n。
你可以将上面两式相减(加)再除以
2
2
2,就可以求得奇数项(偶数项)之和。
LUCAS
如果要求
C
(
n
,
m
)
%
p
C(n,m)\%p
C(n,m)%p,但
n
,
m
n,m
n,m 太大时,就可以用 LUCAS。
(
m
n
)
%
p
≡
(
⌊
m
p
⌋
⌊
n
p
⌋
)
×
(
m
%
p
n
%
p
)
(
m
o
d
p
)
\binom mn\%p\equiv\binom{\lfloor\frac mp\rfloor}{\lfloor\frac np\rfloor}\times\binom{m\%p}{n\%p}\pmod p
(nm)%p≡(⌊pn⌋⌊pm⌋)×(n%pm%p)(modp)
在
n
>
p
n>p
n>p 或
m
>
p
m>p
m>p 时只要反复对第一部分使用该定理即可
使用条件:
p
p
p 是较小质数
P3807 【模板】卢卡斯定理/Lucas 定理
ll inv[N],fac[N];
int lucas(int n,int m,int p)
{
if(n < m)return 0;
if(n < p)return fac[n]*inv[m]*inv[n-m]%p;
return lucas(n/p,m/p,p)*lucas(n%p,m%p,p)%p;
}
int main()
{
int T;cin >> T;
while(T--)
{
int n,m,p;scanf("%d%d%d",&n,&m,&p);n += m;
fac[0] = inv[0] = inv[1] = 1;
for(int i = 1;i <= n;i++)fac[i]=fac[i-1]*i%p;
for(int i = 2;i <= n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
for(int i = 1;i <= n;i++)inv[i]=inv[i-1]*inv[i]%p;
printf("%d\n",lucas(n,m,p));
}
return 0;
}