Note Of Note
- 同余方程中是可以正常进行分数的约分和去分母的
- e x g c d exgcd exgcd 在使用时要保证 a , b a,b a,b 都是非负数
- a a a 与 b b b 互质不代表 a , b a,b a,b 都为质数( 4 4 4 和 5 5 5 互质,但是 4 4 4 不是质数)
- 两个相邻的正整数必定是互质的
- 两个互质的数 a , b a,b a,b 凑不出来(特指用加法)的最大整数为 ( a − 1 ) ( b − 1 ) − 1 (a-1)(b-1)-1 (a−1)(b−1)−1
- 一个大数的约数个数平均是
l
o
g
n
logn
logn 级别,最坏是
n
\sqrt{n}
n 级别,
int
范围内一个数的约数最多为 1600 个 - 1 到 n 的约数个数之和大概是在 n l o g n nlogn nlogn 这个级别
- 1 到 n 之间的质数大概有 n ln n \dfrac{n}{\ln n} lnnn
- 只有完全平方数的因子个数为奇数个
- 任何一个合数 n 必定包含一个不超过 n \sqrt n n 的质因子
TODO
分解质因数,判断质数,分解因数时循环的范围与写法
质数
质数与合数是针对所有**大于 1 1 1 的自然数来定义的(所有小于等于 1 1 1 **的数都不是质数)。
判断质数
用试除法判断,时间复杂度为 O ( n ) O(\sqrt{n}) O(n)
public static boolean isPrime(int n){
if(n < 2) return false;
for(int i = 2;i <= n / i;i ++)
if(n % i == 0) return false;
return true;
}
也可以先把质数筛出来,然后用质数去试除,只需要以 O ( n ) O(n) O(n) 的时间复杂度预处理出 1 1 1 到 n \sqrt n n 里所有素数,每次判断的时间复杂度就可以降低到 O ( n ln n ) O(\frac{\sqrt n}{\ln \sqrt n}) O(lnnn)
分解质因数
算术基本定理
任何一个大于 1 1 1 的自然数 n n n,如果本身不是质数,那么就可以唯一分解为有限个质数的乘积, N = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p n a n N=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} N=p1a1×p2a2×p3a3×⋯×pnan,这里 p 1 < p 2 < p 3 < ⋯ < p n p_1 < p_2 < p_3 < \cdots < p_n p1<p2<p3<⋯<pn 且均为质数,其中指数 a i a_i ai 是正整数
分解质因数
最坏为 O ( n ) O(\sqrt{n}) O(n)
n n n 中最多只包含一个大于 n \sqrt{n} n 的质因子
public static void divide(int n){
for(int i = 2;i <= n / i;i ++){
if(n % i == 0){
int s = 0;
while(n % i == 0){
n /= i;
s ++;
}
printf("%d %d\n",i, s);
}
}
if(n > 1) printf("%d %d\n",n, 1);
printf("\n");
}
可以先预处理出 1 ∼ n 1\sim n 1∼n 中的素数,直接用素数去分解,单次分解的时间复杂度可以降到 O ( n ln n ) O(\frac{\sqrt n}{\ln \sqrt n}) O(lnnn)
这个时间复杂度是比较暧昧的,大概能比 O ( n ) O(\sqrt n) O(n) 低一个数量级, 1 0 6 10^6 106 的数据大概只需要 100 次计算即可。
筛质数
欧拉筛
时间复杂度为 O ( n ) O(n) O(n)
原理说明:
每个合数都只会被自己最小质因子筛掉,假设该最小质因子为 p j p_j pj,则合数 x = p j ∗ i x=p_j*i x=pj∗i,因为 p j p_j pj 是从小到大依次枚举的,如果
- i m o d p j ≠ 0 i\bmod p_j \ne 0 imodpj=0,说明 p j p_j pj 小于 i i i 的所有质因子,那么 p j p_j pj 一定是 p j ∗ i p_j*i pj∗i 的最小质因子
- i m o d p j = 0 i\bmod p_j = 0 imodpj=0,第一个这样的 p j p_j pj 也一定时 p j ∗ i pj*i pj∗i 的最小质因子,而且可以停止枚举了,因为 p j + 1 p_{j+1} pj+1 已经大于了 i i i 的最小质因子了
int primes[N], cnt=0; // primes[]存储所有素数
boolean st[N]; // st[x]存储x是否被筛掉
void getPrimes(int n){
for (int i = 2; i <= n; i ++ ){
if (!st[i]) primes[++cnt] = i;
for (int j = 1; primes[j] <= n / i; j ++ ){
st[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
约数
试除法求约数
public static void getDivisors(int n){
List<Integer> list = new ArrayList<>();
for(int i=1;i<=n/i;i++){
if(n%i==0){
list.add(i);
if(i!=n/i) list.add(n/i);
}
}
list.sort((a,b)->{return a-b;});
for(Integer a : list)
cout.print(a+" ");
cout.println();
}
求 n 的正约数集合像上面这么求的话,时间复杂度为 O ( n ) O(\sqrt n) O(n),如果连续求 1 到 n 的正约数集合的话,就是 O ( n n ) O(n\sqrt n) O(nn)。
我们可以用倍数法来求:
直接枚举因数,然后把因数添加到相应的数的集合里,时间复杂度为 O ( n + n 2 + n 3 + n 4 + ⋯ + n n = O ( n log n ) O(n+\frac{n}{2}+\frac{n}{3}+\frac{n}{4}+\cdots+\frac{n}{n}=O(n\log n) O(n+2n+3n+4n+⋯+nn=O(nlogn)
static List<Integer>[] factor;
public static void getFactors(int n){
factor = new ArrayList[n+1];
for(int i=1;i<=n;i++) factor[i] = new ArrayList<>();
// 枚举因子
for(int i=1;i<=n;i++)
// 枚举倍数
for(int j=1;j<=n/i;j++)
factor[i*j].add(i);
}
约数相关公式
如果 N = p 1 a 1 × p 2 a 2 × p 3 a 3 × ⋯ × p n a n N=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} N=p1a1×p2a2×p3a3×⋯×pnan,那么:
- N N N 的约数个数为 ( a 1 + 1 ) × ( a 2 + 1 ) × ⋯ × ( a n + 1 ) (a_1+1)\times(a_2+1)\times \cdots \times(a_n+1) (a1+1)×(a2+1)×⋯×(an+1)
- N N N 的约数之和为 ( p 1 0 + p 1 1 + ⋯ + p 1 a 1 ) × ⋯ × ( p n 0 + p n 1 + ⋯ + p n a n ) (p_1^0+p_1^1+ \cdots + p_1^{a_1})\times \cdots \times (p_n^0+p_n^1+ \cdots + p_n^{a_n}) (p10+p11+⋯+p1a1)×⋯×(pn0+pn1+⋯+pnan)
以上的公式可以这么理解:
- 约数个数相当于是乘法原理,每个指数都可以选一个值( 0 ∼ a i 0 \sim a_i 0∼ai,共 a i + 1 a_i+1 ai+1个数),只要有一个指数的值不同,数就不一样(算术基本定理),所以全部乘起来
- 约数之和可以这样理解:指数的任意一种不同的组合,出来都是N的一个约数,原本是 p 1 0 ∗ p 2 0 + p 1 0 ∗ p 2 1 + p 1 1 ∗ p 2 0 + p 1 1 ∗ p 2 1 p_1^0*p_2^0+p_1^0*p_2^1+p_1^1*p_2^0+p_1^1*p_2^1 p10∗p20+p10∗p21+p11∗p20+p11∗p21,乘法分配律整理一下就行了
最大公因数
几个基本性质:
- d ∣ a , d ∣ b ⟹ d ∣ a x + b y d\mid a,d \mid b\implies d\mid ax+by d∣a,d∣b⟹d∣ax+by(读作“d能整除a,d能整除b,d就能整除ax+by”)
- a m o d b = a − ⌊ a b ⌋ ∗ b = a − c ∗ b a\bmod b=a- \left\lfloor\dfrac{a}{b}\right\rfloor *b=a-c*b amodb=a−⌊ba⌋∗b=a−c∗b
所以 gcd ( a , b ) = gcd ( b , a − c ∗ b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,a-c*b)=\gcd(b,a\bmod b) gcd(a,b)=gcd(b,a−c∗b)=gcd(b,amodb)
第一个等式两边的因数集合是一样的,左边的最大公因数就是右边的最大公因数( d d d 能整除 a a a, d d d 能整除 b b b, d d d 也就能整除 a − c ∗ b a-c*b a−c∗b)
任何数都可以整除0,所以 b b b 为 0 0 0 时就返回 a a a
public static int gcd(int a,int b){
if(b==0) return a;
else return gcd(b,a%b);
}
两个数的积等于它们最大公约数和它们最小公倍数的积: a × b = gcd ( a , b ) × lcm ( a , b ) a\times b=\gcd(a,b)\times\operatorname{lcm}(a,b) a×b=gcd(a,b)×lcm(a,b)
欧拉函数
定义
ϕ ( n ) \phi(n) ϕ(n): 1 ∼ n 1\sim n 1∼n 中与 n n n 互质的数的个数
ϕ ( 6 ) = 2 \phi(6)=2 ϕ(6)=2
互质: a a a 与 b b b 互质的充要条件是 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1, 1 1 1 和任何数都互质
计算方法
当 n n n 为质数时, ϕ ( n ) = n − 1 \phi(n)=n-1 ϕ(n)=n−1
n
=
p
1
a
1
×
p
2
a
2
×
p
3
a
3
×
⋯
×
p
n
a
n
n=p_1^{a_1}\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n}
n=p1a1×p2a2×p3a3×⋯×pnan
ϕ
(
n
)
=
n
∗
∏
i
=
1
x
(
1
−
1
p
i
)
\phi(n)=n*\prod\limits_{i=1}^x (1-\frac{1}{p_i})
ϕ(n)=n∗i=1∏x(1−pi1)
简单证明
欧拉函数为积性函数,当 m , n m,n m,n 互质时,有 ϕ ( n m ) = ϕ ( n ) ∗ ϕ ( m ) \phi(nm)=\phi(n)*\phi(m) ϕ(nm)=ϕ(n)∗ϕ(m)
由唯一分解定理知, n = × p 2 a 2 × p 3 a 3 × ⋯ × p n a n n=\times p_2^{a_2}\times p_3^{a_3}\times \cdots \times p_n^{a_n} n=×p2a2×p3a3×⋯×pnan,因此 ϕ ( n ) = ϕ ( p 1 a 1 ) × ϕ ( p 2 a 2 ) ⋯ × ϕ ( p n a n ) \phi(n)=\phi(p_1^{a_1})\times \phi(p_2^{a_2})\cdots\times \phi(p_n^{a_n}) ϕ(n)=ϕ(p1a1)×ϕ(p2a2)⋯×ϕ(pnan)
现考虑 ϕ ( p i a i ) \phi(p_i^{a_i}) ϕ(piai) 的求法,回归到定义,即为小于等于 p i a i p_i^{a_i} piai 的正整数中与其互质的数,从 1 1 1 到 p i a i p_i^{a_i} piai 一共有 p i a i p_i^{a_i} piai 个数字,其中与 p i a i p_i^{a_i} piai 不互质的有 p i , 2 p i , 3 p i , ⋯ , p i a i − 1 ∗ p i p_i,2p_i,3p_i,\cdots,p_i^{a_i-1}*p_i pi,2pi,3pi,⋯,piai−1∗pi,共 p i a i − 1 p_i^{a_i-1} piai−1 项
所以 ϕ ( p i a i ) = p i a i − p i a i − 1 = p i a i ∗ ( 1 − 1 p i ) \phi(p_i^{a_i})=p_i^{a_i}-p_i^{a_i-1}=p_i^{a_i}*(1-\dfrac{1}{p_i}) ϕ(piai)=piai−piai−1=piai∗(1−pi1)
由此得证
求某个值的欧拉函数( O ( n ) O(\sqrt{n}) O(n)):
public static int phi(int a){
int res = a;
for(int i=2;i<=a/i;i++){
if(a%i==0){
res = res*(i-1)/i;
while(a%i==0) a/=i;
}
}
if(a>1) res = res*(a-1)/a;
return res;
}
线性筛求欧拉函数( O ( n ) O(n) O(n)):
public static void getEuler(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!ck[i]){
primes[++cnt]=i;
phi[i]=i-1;
}
for(int j=1;j<=cnt;j++){
ck[i*primes[j]]=true;
if(i%primes[j]==0){
phi[i*primes[j]] = phi[i]*primes[j];
break;
}else
phi[i*primes[j]] = phi[i]*(primes[j]-1);
}
}
}
模意义下的数和运算
定义: 对于整数 a , b a,b a,b,满足 b > 0 b>0 b>0,则存在唯一的整数 q , r q,r q,r,满足 a = b q + r a=bq+r a=bq+r,其中称 q q q 为商, r r r 为余数
说人话, a ÷ b a\div b a÷b 的商和余数是固定的
这个定义可以将同余式转换为不定方程
模意义下的运算法则
( a + b ) m o d M = ( a m o d M + b m o d M ) m o d M (a+b)\bmod M = (a\bmod M +b\bmod M)\bmod M (a+b)modM=(amodM+bmodM)modM
( a − b ) m o d M = ( a m o d M − b m o d M ) m o d M (a-b)\bmod M = (a\bmod M -b\bmod M)\bmod M (a−b)modM=(amodM−bmodM)modM
(
a
×
b
)
m
o
d
M
=
(
a
m
o
d
M
×
b
m
o
d
M
)
m
o
d
M
(a\times b)\bmod M = (a\bmod M \times b\bmod M)\bmod M
(a×b)modM=(amodM×bmodM)modM
模意义下除法比较特殊
[!summary]
这几个运算法则用人话说就是:
- 全部加完后再取模等于边加边模
- 全部减完后再取模等于边减边模
- 全部乘完后再取模等于边乘边模
- 全部除完后再取模不等于边除边模
同余
定义:若两数 x , y x,y x,y 除以 b b b 的余数相等,则称 x , y x,y x,y 模 b b b 同余,记做 x ≡ y ( m o d b ) x\equiv y \pmod{b} x≡y(modb)
重要性质:
- 同加性,同乘性,同幂性
- x ≡ y ( m o d b ) ⟺ b ∣ ( x − y ) x\equiv y \pmod{b} \Longleftrightarrow b\mid(x-y) x≡y(modb)⟺b∣(x−y) ( b b b 能整除 x − y x-y x−y )
欧拉定理与费马小定理
欧拉定理
若 a a a 与 n n n 互质,则 a φ ( n ) ≡ 1 ( m o d n ) a^{\varphi (n)} \equiv 1 \pmod{n} aφ(n)≡1(modn)
费马小定理
若 p p p 为质数,且 a a a 与 p p p 互质,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \pmod p ap−1≡1(modp)
快速幂
二进制思想
public static long qmi(long a,long k,long m){
long res=1;
while(k>0){
if((k&1)==1) res=res*a%m;
a=a*a%m;
k>>=1;
}
return res;
}
扩展欧几里得算法
裴蜀定理
对于任意正整数 a , b a,b a,b,设他们的最大公约数 gcd ( a , b ) = d \gcd(a,b)=d gcd(a,b)=d,则一定存在非零整数 x , y x,y x,y,使得 a x + b y = d ax+by=d ax+by=d
- d d d 是 a a a 和 b b b 能凑出来的最小正整数
- 对于整数 a , b , c a,b,c a,b,c, a x + b y = c ax+by=c ax+by=c 有整数解当且仅当 gcd ( a , b ) ∣ c \gcd(a,b) \mid c gcd(a,b)∣c
重要推论: a , b a,b a,b 互质的充要条件是存在整数 x , y x,y x,y 使 a x + b y = 1 ax+by=1 ax+by=1
说人话:两个数最小能”凑“出来的正数是他们俩的最大公倍数,并且所有能凑出来的数都是最大公倍数的倍数
这是两个数的情况,裴蜀定理也可以推广到 n n n 个数的情况
扩展欧几里得算法
public static int x,y;
public static int exgcd(int a, int b){
if(b == 0){
x = 1;y = 0;
return a;
}
int d = exgcd(b, a % b);
// 本层 ax1,by1 下一层 x0,y0
// x1 = y0,y1 = x0-(a/b)*y0
int temp = x;
x = y;
y = temp - a / b * y;
return d;
}
此时得出的
x
0
,
y
0
x_0,y_0
x0,y0 为
a
x
+
b
y
=
gcd
(
a
,
b
)
ax+by=\gcd(a,b)
ax+by=gcd(a,b) 的一组解,设
x
,
y
x,y
x,y 的最小公倍数为
gcd
(
a
,
b
)
=
d
\gcd(a,b)=d
gcd(a,b)=d,则有:
{
x
=
x
0
+
b
d
⋅
k
y
=
y
0
−
a
d
⋅
k
\begin{cases} x=x_0+\dfrac{b}{d}\cdot k\\y=y_0-\dfrac{a}{d}\cdot k\end{cases}
⎩
⎨
⎧x=x0+db⋅ky=y0−da⋅k
注意这里的
x
,
y
x,y
x,y 是
a
x
+
b
y
=
gcd
(
a
,
b
)
ax+by=\gcd(a,b)
ax+by=gcd(a,b) 的所有解,不是
a
x
+
b
y
=
c
ax+by=c
ax+by=c 的所有解
如何求 a x + b y = c ax+by=c ax+by=c 的所有解:
- 先求出 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组特解 x 0 , y 0 x_0,y_0 x0,y0
- 再把 x 0 , y 0 x_0,y_0 x0,y0 一起扩大 c gcd ( a , b ) \dfrac{c}{\gcd(a,b)} gcd(a,b)c倍,得到 x 1 , y 1 x_1,y_1 x1,y1
- 再计算 a x + b y = 0 ax+by=0 ax+by=0 的所有解,即 x = b gcd ( a , b ) , y = − a gcd ( a , b ) x=\dfrac{b}{\gcd(a,b)},y=-\dfrac{a}{\gcd(a,b)} x=gcd(a,b)b,y=−gcd(a,b)a
- 最后得到 a x + b y = c ax+by=c ax+by=c 的所有解
所以扩展欧几里得算法常用于求解线性同余方程,即给定 a , b , c a,b,c a,b,c,求方程 a x ≡ b ( m o d c ) ax \equiv b \pmod c ax≡b(modc) 的解,等价于求 a x + c y = b ax+cy=b ax+cy=b 的解
逆元
定义:若 a × x ≡ 1 ( m o d p ) a\times x \equiv 1 \pmod p a×x≡1(modp),则称 x x x 为 a a a 在模 p p p 意义下的乘法逆元,记为 a − 1 ( m o d p ) a^{-1} \pmod p a−1(modp)
( a ÷ b ) m o d M = ( a m o d M × b − 1 ( m o d M ) ) m o d M (a \div b) \bmod M=(a\bmod M \times b^{-1}\pmod M)\bmod M (a÷b)modM=(amodM×b−1(modM))modM
如果要分开算的话,才需要这么算,比如 4 ÷ 2 = 2 4\div2=2 4÷2=2,直接用 2 2 2 带入即可
定理: 在大于等于 0 0 0,小于 p p p 的范围内,模 p p p 的逆元(若存在)是唯一的
结论: a a a 在模 p p p 意义下的逆元存在当且仅当 a a a 与 p p p 互质
计算方法
费马小定理
若 p p p 为质数,且 a a a 与 p p p 互质,则 a a a 在模 p p p 意义下的乘法逆元为 a p − 2 a^{p-2} ap−2
a
p
−
2
a^{p-2}
ap−2 可以用快速幂求解:qmi(a,p-2,p)
扩展欧几里得算法
a × x ≡ 1 ( m o d p ) a\times x \equiv 1 \pmod p a×x≡1(modp) 与 a x + p y = 1 ax+py=1 ax+py=1 等价:
a × x ≡ 1 ( m o d p ) ⟺ p ∣ ( a x − 1 ) ⟺ ∃ y , 使得 ( a x − 1 ) = p y ⟺ ∃ y , 使得 a x − p y = 1 ⟺ ∃ y , 使得 a x + p y = 1 \begin{aligned}a\times x \equiv 1 \pmod p & \Longleftrightarrow p\mid(ax-1) \\ & \Longleftrightarrow \exists y,\text{使得} (ax-1)=py\\ & \Longleftrightarrow \exists y,\text{使得} ax-py=1 \\ & \Longleftrightarrow \exists y,\text{使得} ax+py=1\end{aligned} a×x≡1(modp)⟺p∣(ax−1)⟺∃y,使得(ax−1)=py⟺∃y,使得ax−py=1⟺∃y,使得ax+py=1
第一步转化是同余的重要性质,第二步转化是模的定义
这个式子是一个关于 a , p a,p a,p 的不定方程,还可以由裴蜀定理发现, a a a 在模 p p p 意义下的逆元当且仅当 gcd ( a , p ) = 1 \gcd(a,p)=1 gcd(a,p)=1,即 a a a 与 p p p 互质时才成立
public static int inv(int a,int p){
exgcd(a, p);
return x;
}
中国剩余定理
中国剩余定理可以求解下面这样一类同余方程组:
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\begin{cases}x\equiv a_1 \pmod {m_1} \\ x\equiv a_2 \pmod {m_2} \\ \, \quad\vdots \\ x\equiv a_n \pmod {m_n} \end{cases}
⎩
⎨
⎧x≡a1(modm1)x≡a2(modm2)⋮x≡an(modmn)
其中, m 1 , m 2 , ⋯ , m n m_1,m_2,\cdots,m_n m1,m2,⋯,mn 两两互质,则对于任意的整数 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots,a_n a1,a2,⋯,an 上面这个方程组有解,解的构造方式为:
- 设 M = ∏ i = 1 n m i M=\prod_{i=1}^nm_i M=∏i=1nmi,设 M i = M m i M_i=\dfrac{M}{m_i} Mi=miM
- 设 t i = M i − 1 t_i=M_i^{-1} ti=Mi−1 为 M i M_i Mi 模 m i m_i mi 的逆元(易知 M i M_i Mi 和 m i m_i mi 是互质的,所以逆元一定存在)
- x x x 的通解为 x = k M + ∑ i = 1 n a i t i M i , k ∈ Z x=kM+\sum_{i=1}^na_it_iM_i,k\in Z x=kM+∑i=1naitiMi,k∈Z
组合数学
组合数
C a b C_a^b Cab 代表从 a a a 个物品中选出 b b b 个的方案数(不考虑顺序)
基本计算公式:
C a b = a ! b ! ( b − a ) ! C_a^b=\dfrac{a!}{b!(b-a)!} Cab=b!(b−a)!a!
递推求组合数
时间复杂度为 O ( n 2 ) O(n^2) O(n2)
适用范围:
- 1 ≤ n ≤ 10000 1\le n \le 10000 1≤n≤10000
- 1 ≤ b ≤ a ≤ 2000 1 \le b \le a \le 2000 1≤b≤a≤2000
#include<iostream>
using namespace std;
const int mod = 1e9+7;
long long f[2010][2010];
int main()
{
//预处理
for(int i=0;i<=2000;i++)
{
for(int j=0;j<=i;j++)
{
if(j==0) f[i][j]=1;
else f[i][j]=(f[i-1][j-1]+f[i-1][j])%mod;
}
}
int n;
cin>>n;
while(n--)
{
int a,b;
cin>>a>>b;
printf("%ld\n",f[a][b]);
}
}
预处理逆元求组合数
时间复杂度为 O ( a ∗ l o g ( m o d ) ) O(a*log(mod)) O(a∗log(mod))
适用范围:
- 1 ≤ n ≤ 10000 1\le n \le 10000 1≤n≤10000
- 1 ≤ b ≤ a ≤ 1 0 5 1 \le b \le a \le 10^5 1≤b≤a≤105
由基本公式得(在模的情况下进行)
C a b = a ! b ! ( b − a ) ! = a ! × b ! − 1 × ( b − a ) ! − 1 C_a^b=\dfrac{a!}{b!(b-a)!}=a!\times b!^{-1}\times (b-a)!^{-1} Cab=b!(b−a)!a!=a!×b!−1×(b−a)!−1
static int n,m,k;
static long x,y,z;
static long[] fac,infac;
static final int INF = 0x3f3f3f3f, MOD = (int) 1e9+7;
public static void main(String[] args) throws IOException {
int lim = (int) 1e5;
fac = new long[lim+1];
infac = new long[lim+1];
fac[0]=infac[0]=1;
for(int i=1;i<=lim;i++){
fac[i]=i*fac[i-1]%MOD;
infac[i]=infac[i-1]*qmi(i,MOD-2,MOD)%MOD;
}
n = nextInt();
for(int i=1;i<=n;i++){
int a =nextInt(),b = nextInt();
long res = fac[a]*infac[b]%MOD*infac[a-b]%MOD;
cout.println(res);
}
cout.flush();
}// End of main
public static long qmi(long a,long k,long p){
long res = 1;
while(k>0){
if((k&1)==1) res = res*a%p;
a = a*a%p;
k>>=1;
}
return res;
}
Lucas定理求组合数
C a b ≡ C a p b p ⋅ C a m o d p b m o d p ( m o d p ) C_a^b\equiv C_{\frac{a}{p}}^{\frac{b}{p}} \cdot C_{a \bmod p}^{b \bmod p} \pmod p Cab≡Cpapb⋅Camodpbmodp(modp)
适用范围:
- 1 ≤ n ≤ 20 1\le n \le 20 1≤n≤20
- 1 ≤ b ≤ a ≤ 1 0 18 1 \le b \le a \le 10^{18} 1≤b≤a≤1018
- 1 ≤ p ≤ 1 0 5 1\le p \le 10^5 1≤p≤105
[!important]
long
类型的读入最好用 Scanner,要不然你咋挂的都不知道
static int n,m,k;
static long x,y,z;
static long[] fac,infac;
static final int INF = 0x3f3f3f3f, MOD = (int) 1e9+7;
public static void main(String[] args) throws IOException {
Scanner cin = new Scanner(System.in);
n = cin.nextInt();
while(n-->0){
long a = cin.nextLong(),b = cin.nextLong(),p=cin.nextLong();
cout.println(lucas(a, b, p));
}
cout.flush();
}// End of main
public static long lucas(long a,long b,long p){
if(a<p && b<p) return C(a,b,p);
// a%p后肯定是<p的,所以可以用C(),但a/p后不一定<p 所以用lucas继续递归
return lucas(a/p, b/p, p)*C(a%p,b%p,p)%p;
}
public static long C(long a,long b,long p){
long res = 1;
// C_a^b 的公式求法就是
// a!/(b!(a-b)!) = (a-b+1)*...*a / b! 分子有b项
// i -> 1 - b j -> a - a-b+1
for(long i=1,j=a;i<=b;i++,j--){
res = res*j%p;
res = res*qmi(i,p-2,p)%p;
}
return res;
}
public static long qmi(long a,long k,long p){
long res = 1;
while(k>0){
if((k&1)==1) res = res*a%p;
a = a*a%p;
k>>=1;
}
return res;
}
卡特兰数
卡特兰数计算公式:
C
2
n
n
−
C
2
n
n
−
1
=
C
2
n
n
n
−
1
C_{2n}^n-C_{2n}^{n-1}=\dfrac{C_{2n}^n}{n-1}
C2nn−C2nn−1=n−1C2nn
f ( n ) = f ( 0 ) f ( n − 1 ) + f ( 1 ) f ( n − 2 ) + + f ( n − 1 ) f ( 0 ) f(n) = f(0)f(n-1) + f(1)f(n-2) + + f(n-1)f(0) f(n)=f(0)f(n−1)+f(1)f(n−2)++f(n−1)f(0)
1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862,
应用:
- 出栈次序
- n对括号正确匹配数目
- 给定节点组成二叉搜索树
- 在圆上选择 2 n 2n 2n 个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数
- 求一个凸多边形区域划分成三角形区域的方法数
期望
在OI中,期望一般指的就是达到结果的期望,最朴素的计算是每次可能结果的概率乘以其结果的总和