常用算法代码模板 (4) :数学知识

常用算法模板——数学知识:质数、约数、欧拉函数、快速幂、扩展欧几里得算法、高斯消元、组合数


常用算法代码模板 (1) :基础算法
常用算法代码模板 (2) :数据结构
常用算法代码模板 (3) :搜索与图论
常用算法代码模板 (4) :数学知识
算法选择——由数据范围反推算法时间复杂度



1 质数

定义:在大于 1 1 1 的整数中,只包含 1 1 1 和本身这两个约数的数称为质数(或素数

1.1 试除法判定质数

时间复杂度: O ( n ) O(\sqrt n) O(n )

bool is_prime(int x) {
    if (x < 2) return false;
    for (int i = 2; i <= x / i; i++)	// 枚举到sqrt(x)
        if (x % i == 0)
            return false;
    return true;
}

1.2 试除法分解质因数

时间复杂度: O ( n ) O(\sqrt n) O(n )

vector<pair<int, int> > primes;	// 存储质因数及其个数

void divide(int x) {
    for (int i = 2; i <= x / i; i++)	// 枚举到sqrt(x)
        if (x % i == 0) {
            int cnt = 0;	// cnt记录质因子i的个数
            while (x % i == 0) {
                x /= i;
                cnt++;
            }
            primes.push_back({i, cnt});
        }
  
    if (x > 1) primes.push_back({x, i});	// 原理:x中只包含1个大于sqrt(x)的质因子
}

1.3 筛法求素数表

1.3.1 埃氏筛法

时间复杂度: O ( n log ⁡ log ⁡ n ) O(n\log\log n) O(nloglogn)

int primes[N], len;	// 存储所有素数
bool st[N];			// st[i]标记数i是否被筛掉

/* 埃氏筛法求[2, n]上所有素数 */
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {	// 仅遍历未被筛去的数,且只筛它的倍数
            primes[len++] = i;
            for (int j = i + i; j <= n; j += i)	// 筛去i的倍数,朴素法遍历全部倍数
                st[j] = true;
        }
    }
}

1.3.2 线性筛法

时间复杂度: O ( n ) O(n) O(n)

核心思想:每个合数只会被其最小质因子筛掉。对于 i i i 和素数 P j P_j Pj ,若 i   m o d   P j = 0 i \bmod P_j=0 imodPj=0 ,且 P j P_j Pj i i i 的最小质因子,即一定是 P j ⋅ i P_j \cdot i Pji 的最小质因子。

int primes[N], len;	// 存储所有素数
bool st[N];			// st[i]标记数i是否被筛掉

/* 线性筛法求[2, n]上所有素数 */
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[len++] = i;
        for (int j = 0; primes[j] <= n / i; j++) {	// primes[j] * i <= n
            st[primes[j] * i] = true;		// 每个合数只会被其最小质因子筛掉
            if (i % primes[j] == 0) break;	// 保证primes[j]一定是primes[j] * i的最小质因子
        }
    }
}

2 约数

2.1 试除法求所有约数

时间复杂度:取决于排序函数,试除的消耗是 O ( n ) O(\sqrt n) O(n )

/* 求所有约数(去重且递增排序) */
vector<int> get_divisors(int x) {
    vector<int> res;
    for (int i = 1; i <= x / i; i++)	// 枚举到sqrt(x)
        if (x % i == 0) {	// 若i为x的约数,则x/i也是x的约数
            res.push_back(i);
            if (i != x / i) res.push_back(x / i);	// 不重复存储约数sqrt(x)
        }
    sort(res.begin(), res.end());
    return res;
}

2.2 约数个数、约束之和

N = p 1 α 1 p 2 α 2 ⋯ p k α k N = p_1^{\alpha_1} p_2^{\alpha_2} \cdots p_k^{\alpha_k} N=p1α1p2α2pkαk ,其中 p i p_i pi 是试除法求得的约数(个数为 α i \alpha_i αi ),则

  • 约数个数 ∏ i = 1 k ( α i + 1 ) = ( α 1 + 1 ) ( α 2 + 1 ) ⋯ ( α k + 1 ) \prod^k\limits_{i=1}(\alpha_i+1)=(\alpha_1+1)(\alpha_2+1)\cdots(\alpha_k+1) i=1k(αi+1)=(α1+1)(α2+1)(αk+1)
  • 约数之和 ∏ i = 1 k ( ∑ j = 0 α i p i j ) = ( p 1 0 + p 1 1 + ⋯ + p 1 α 1 ) ⋯ ( p k 0 + p k 1 + ⋯ + p k α k ) \prod^k\limits_{i=1}(\sum^{\alpha_i}\limits_{j=0}p_i^j)=(p_1^0+p_1^1+\cdots+p_1^{\alpha_1})\cdots(p_k^0+p_k^1+\cdots+p_k^{\alpha_k}) i=1k(j=0αipij)=(p10+p11++p1α1)(pk0+pk1++pkαk)
typedef long long LL;

const int MOD = 1e9 + 7;		// 防止结果过大而溢出

int x;
vector<pair<int, int> > primes;	// 用4.1(2) divide()返回的数组,存储约数p_k及其个数a_k

/* 求约数个数 */
LL cnt = 1;
for (auto prime : primes) cnt = cnt * (prime.second + 1) % MOD;

/* 求约数之和 */
LL sum = 1;
for (auto prime: primes) {
    int p = prime.first, a = prime.second;	// 约数p与指数a
    LL t = 1;	// 记录p^0+...+p^a
    while (a--) t = (t * p + 1) % MOD;		// 秦九韶算法
    sum = sum * t % MOD;
}

2.3 欧几里得算法

整除的性质:若 d ∣ a d \mid a da d ∣ b d \mid b db ,则 d ∣ a x + b y d \mid ax+by dax+by

欧几里得算法(辗转相除法)求最大公约数: gcd ⁡ ( a , b ) = gcd ⁡ ( b , a   m o d   b ) \gcd(a,b)=\gcd(b,a \bmod b) gcd(a,b)=gcd(b,amodb)

时间复杂度: O ( log ⁡ n ) O(\log n) O(logn)

int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;	// gcd(a, 0) = a
}

2.4 求最小公倍数

lcm ( a , b ) = a b gcd ⁡ ( a , b ) \text{lcm}(a,b)=\frac{ab}{\gcd(a,b)} lcm(a,b)=gcd(a,b)ab

int lcm(int a, int b) {
	return a / gcd(a, b) * b;
}

3 欧拉函数

定义: 1 1 1 ~ N N N 中与 N N N 互质的数的个数称为欧拉函数,记为 ϕ ( N ) \phi(N) ϕ(N)

求法:若 N = p 1 a 1 p 2 a 2 ⋯ p m a m N=p_1^{a_1}p_2^{a_2}\cdots p_m^{a_m} N=p1a1p2a2pmam ,则 ϕ ( N ) = N ∏ i = 1 m ( 1 − 1 p i ) \phi(N)=N\prod^m\limits_{i=1}(1-\frac1{p_i}) ϕ(N)=Ni=1m(1pi1) 。特别地,对于质数 p p p ϕ ( p ) = p − 1 \phi(p)=p-1 ϕ(p)=p1

欧拉定理:若 a a a m m m 互质,即 gcd ⁡ ( a , m ) = 1 \gcd(a,m)=1 gcd(a,m)=1 ,则 a ϕ ( m ) ≡ 1 ( m o d m ) a^{\phi(m)}\equiv 1 \pmod m aϕ(m)1(modm)

费马小定理:若 p p p 为质数,则 a ϕ ( p ) ≡ 1 ( m o d p ) ⇒ a p − 1 ≡ 1 ( m o d p ) ⇒ a p ≡ a ( m o d p ) a^{\phi(p)}\equiv1\pmod p \Rightarrow a^{p-1}\equiv 1\pmod p \Rightarrow a^p\equiv a\pmod p aϕ(p)1(modp)ap11(modp)apa(modp)

3.1 求欧拉函数

时间复杂度: O ( n ) O(\sqrt n) O(n )

int phi(int x) {
	int res = x;
	for (int i = 2; i <= x / i; i++)	// 试除法分解质因数
		if (x % i == 0) {
			res = res / i * (i - 1);	// 化简(1 - 1 / i)所得
			while (x % i == 0) x /= i;
		}

	if (x > 1) res = res / x * (x - 1);
	return res;
}

3.2 筛法求欧拉函数表

时间复杂度: O ( n ) O(n) O(n)

int primes[N], len;	// 存储所有素数
int euler[N];		// euler[x]存储x的欧拉函数
bool st[N];			// st[x]存储x是否被筛掉

/* 线性筛法求[1, n]上所有数的欧拉函数 */
void get_eulers(int n) {
	euler[1] = 1;	// 规定1与任何数互质
	for (int i = 2; i <= n; i++) {
		if (!st[i]) {
			primes[len++] = i;
			euler[i] = i - 1;	// 若i为质数,则phi(i)=i-1(1~i-1均与i互质)
		}
		for (int j = 0; primes[j] <= n / i; j++) {	// p_j * i <= n
			st[primes[j] * i] = true;
			if (i % primes[j] == 0) {	// 若p_j是i的最小质因子,则一定是p_j * i的最小质因子
				euler[primes[j] * i] = euler[i] * primes[j];	// 因此phi(p_j * i)的\prod部分与phi(i)完全相同
				break;
			}
			euler[primes[j] * i] = euler[i] * (primes[j] - 1);	// 否则phi(p_j * i) = p_j * phi(i) * (1 - 1 / p_j)
		}
	}
}

4 快速幂

a k   m o d   p a^k \bmod p akmodp,时间复杂度: O ( log ⁡ k ) O(\log k) O(logk)

思想: a k   m o d   p = ∏ i = 0 log ⁡ k a 2 i   m o d   p a^k\bmod p=\prod\limits^{\log k}_{i=0}a^{2^i}\bmod p akmodp=i=0logka2imodp a 2 i + 1   m o d   p = ( a 2 i   m o d   p ) 2   m o d   p a^{2^{i+1}}\bmod p=(a^{2^i}\bmod p)^2\bmod p a2i+1modp=(a2imodp)2modp

核心:求 k k k二进制表示,即 a k   m o d   p = ∏ i ∈ { i ∣ k [ i ] = 1 } a 2 i   m o d   p a^k\bmod p=\prod\limits_{i\in\{i|k[i]=1\}} a^{2^i}\bmod p akmodp=i{ik[i]=1}a2imodp

应用:求模为质数的逆元(逆元的定义: a x ≡ 1 ( m o d m ) ax\equiv 1\pmod m ax1(modm) a , m a,m a,m 互质,则称 x x x a a a m m m 的逆元,记为 a − 1 a^{-1} a1 )。当模为质数 p p p 时,由费马小定理得 a ϕ ( p ) ≡ 1 ( m o d p ) ⇒ a p − 1 ≡ 1 ( m o d p ) ⇒ a ⋅ a p − 2 = 1 ( m o d p ) a^{\phi(p)}\equiv1\pmod p \Rightarrow a^{p-1}\equiv 1\pmod p \Rightarrow a\cdot a^{p-2}=1\pmod p aϕ(p)1(modp)ap11(modp)aap2=1(modp),故此时 a − 1 = a p − 2 a^{-1}=a^{p-2} a1=ap2 ,可用快速幂求得。

typedef long long LL;

/* 求a^k mod p */
LL qpow(int a, int k, int p) {
	LL res = 1, t = a;	// t记录a^2^i,其中i>=0,表示逻辑上当前迭代至k的第i位
	while (k) {
		if (k & 1) res = res * t % p;	// 当k末位(k[i])为1时,结果乘上a^2^i mod p
		t = t * t % p;	// 更新操作 t <- a^2^(i+1) mod p = (a^2^i mod p)^2 mod p
		k >>= 1;		// k去掉当前末位,使得逻辑上i++
	}
	return res;
}

5 扩展欧几里得算法

裴蜀定理:对于任意正整数 a , b a, b a,b ,存在非零整数 x , y x,y x,y ,使得 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 满足 a x 0 + b 0 y = d ax_0+b_0y=d ax0+b0y=d ①,其中 d = gcd ⁡ ( a , b ) d=\gcd(a,b) d=gcd(a,b) 。原方程可化为 a ( x − b d ) + b ( y + a d ) = d a(x-\frac bd)+b(y+\frac ad)=d a(xdb)+b(y+da)=d ②。由①②可得通解为 x = x 0 − b d k ,   y = y 0 + a d k ,   k ∈ Z + x=x_0-\frac bdk,\ y=y_0+\frac adk,\ k\in \mathbb{Z^+} x=x0dbk, y=y0+dak, kZ+

应用:求解线性同余方程。对于方程 a x ≡ b ( m o d m ) ax\equiv b\pmod m axb(modm) ∃ y ∈ Z + , a x = m y + b   ⇒ y ′ = − y a x + m y ′ = b \exist y\in\mathbb{Z^+},ax=my+b\ \xRightarrow{y'=-y} ax+my'=b yZ+,ax=my+b y=y ax+my=b,该方程有解的充要条件为 gcd ⁡ ( a , m ) ∣ b \gcd(a,m)|b gcd(a,m)b ,此时可用扩展欧几里得算法exgcd(a, b, x, y')求得一组特解,进而求得原方程的解。特别地,求 a a a m m m 的逆元即为 b = 1 b=1 b=1 的情况。

中国剩余定理:对于两两互质的 k k k 个数 m 1 , m 2 , ⋯   , m k m_1,m_2,\cdots,m_k m1,m2,,mk ,线性同余方程组 x ≡ a i ( m o d m i ) x\equiv a_i\pmod{m_i} xai(modmi) 的通解为 x = a 1 M 1 M 1 − 1 + a 2 M 2 M 2 − 1 + ⋯ + a k M k M k − 1 x=a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+\cdots+a_kM_kM_k^{-1} x=a1M1M11+a2M2M21++akMkMk1,其中 M = m 1 m 2 ⋯ m k ,   M i = M m i , M=m_1m_2\cdots m_k,\ M_i=\frac{M}{m_i}, M=m1m2mk, Mi=miM, M i − 1 M_i^{-1} Mi1 M i M_i Mi m i m_i mi 的逆元(可通过解 M i x ≡ 1 ( m o d m i ) M_ix\equiv1\pmod{m_i} Mix1(modmi) 求得) ,   i = 1 , 2 , ⋯   , k ,\ i=1,2,\cdots,k , i=1,2,,k

/* 求一组x, y特解,满足a*x + b*y = gcd(a, b)。函数返回最大公约数 */
int exgcd(int a, int b, int &x, int &y) {	// x, y用引用型
	if (!b) {	// gcd(a, 0) = a,此时a*x + 0*y = a的通解为(1, 任何数)
		x = 1, y = 0;	// 这里传回特解(1, 0)
		return a;
	}
	int d = exgcd(b, a % b, y, x);	// 将x、y翻转(便于对比条件),使得b*y + (a mod b)*x = gcd(b, a mod b) = gcd(a, b)
	y -= (a / b) * x;	// a mod b = a - [a/b]*b,代入上式化简得a*x + b*(y - [a/b]*x) = d,对比条件可知y的变化量!
	return d;
}

6 高斯消元

化增广矩阵为最简行阶梯形矩阵,解 n n n 元线性方程组,时间复杂度: O ( n 3 ) O(n^3) O(n3)

const double eps = 1e-6;

int n;
double a[N][N];	// n*(n+1)的增广矩阵 a[0 ... n-1][0 ... n]

/* 0:有唯一解(此时将增广矩阵化为最简行阶梯形矩阵),1:有无穷多组解,2:无解 */
int gauss() {
	int c, r;	// 枚举的列、行(同时也记录实际方程个数)
	for (c = 0, r = 0; c < n; c++) {	// 枚举每一列c,最终化为行阶梯形矩阵
		int t = r;
		for (int i = r; i < n; i++)	// 寻找绝对值最大的行,记录于t
			if (fabs(a[i][c]) > fabs(a[t][c]))
				t = i;
				
		if (fabs(a[t][c]) < eps) continue;	// 若为0则无需消元,跳至下一列

		for (int i = c; i <= n; i++) swap(a[t][i], a[r][i]);	// 将绝对值最大的行t换到最顶端的当前行r
		for (int i = n; i >= c; i--) a[r][i] /= a[r][c];	// 将当前行r同除以该行首a[r][c],使得行r首非零元变成1
		for (int i = r + 1; i < n; i++)	// 用当前行r将行r首非零元该列下方所有元素消成0
			if (fabs(a[i][c]) > eps)	// 若为0则无需再遍历操作该行,节省时间
				for (int j = n; j >= c; j--)	// 行i同减行r各列同列元a[r][j]乘以行i首非零元a[i][c](为了消之为0)
					a[i][j] -= a[r][j] * a[i][c];

		r++;	// 完成本列c消元操作后才跳至下一行
	}

	if (r < n) {	// 若化简后的方程个数小于n(最后n-r行系数阵部分全为0,即0行),则有无穷多组解或无解
		for (int i = r; i < n; i++)	// 若某行左边为0而右边非0,则直接判无解
			if (fabs(a[i][n]) > eps)
				return 2;	// 无解
		return 1;	// 有无穷多组解
	}

	for (int i = n - 1; i >= 0; i--)	// 有唯一解则化为最简行阶梯形矩阵,列n所存的即为解
		for (int j = i + 1; j < n; j++)	// 同化行阶梯形操作,用各行首非零元将其列上上全部元素消为0
			a[i][n] -= a[i][j] * a[j][n];
	return 0;	// 有唯一解
}

7 组合数

组合数的定义: C n m = n × ( n − 1 ) × ⋯ × ( n − m + 1 ) 1 × 2 × ⋯ × m = n ! ( n − m ) ! m !   ( m ≤ n ) \text{C}_n^m=\frac{n\times(n-1)\times\cdots\times(n-m+1)}{1\times2\times\cdots\times m}=\frac{n!}{(n-m)!m!}\ (m\le n) Cnm=1×2××mn×(n1)××(nm+1)=(nm)!m!n! (mn) ,或写作 C ( n , m ) \text{C}(n,m) C(n,m) ( n m ) {n \choose m} (mn)

互补性: C n m = C n n − m \text{C}_n^m=\text{C}_n^{n-m} Cnm=Cnnm

递推式: C n m = C n − 1 m + C n − 1 m − 1 \text{C}_n^m=\text{C}_{n-1}^m+\text{C}_{n-1}^{m-1} Cnm=Cn1m+Cn1m1

7.1 递推法求组合数

适用于处理 1 0 5 10^5 105 量级的数据量、 1 ≤ m ≤ n ≤ 2000 1≤m≤n≤2000 1mn2000 的情况,时间复杂度: O ( n 2 ) O(n^2) O(n2)

const int MOD = 1e9 + 7;	// 防止结果过大溢出

int c[N][N];	// c[i][j]即为C(i, j),表示从i个不同元素中取j个的方案数

/* 计算C(0, 0) ~ C(N-1, N-1) */
for (int i = 0; i < N; i++)
	for (int j = 0; j <= i; j++)
		if (!j) c[i][j] = 1;	// 规定“取0个/不取”算作只有1种方案
		else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;

7.2 通过逆处理逆元的方式求组合数

C n m = n ! ⋅ ( ( n − m ) ! ) − 1 ⋅ ( m ! ) − 1 \text{C}_n^m=n!\cdot((n-m)!)^{-1}\cdot (m!)^{-1} Cnm=n!((nm)!)1(m!)1 ,其中 ( ( n − m ) ! ) − 1 , ( m ! ) − 1 ((n-m)!)^{-1},(m!)^{-1} ((nm)!)1,(m!)1 分别为 ( n − m ) ! , m ! (n-m)!,m! (nm)!,m! p p p 的逆元, p p p 为质数。由费马小定理可知逆元 a − 1 = a p − 2 a^{-1}=a^{p-2} a1=ap2

适用于处理 1 0 4 10^4 104 量级的数据量、 1 ≤ m ≤ n ≤ 1 0 5 1≤m≤n≤10^5 1mn105 的情况,时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)

typedef long long LL;		// 预处理时临时防爆int

const int MOD = 1e9 + 7;	// 防止结果过大溢出

int fact[N];	// fact[i]存储i的阶乘再取模
int infact[N];	// infact[i]存储fact[i]的模为质数的逆元再取模

/* 快速幂模板,用来求模为质数的逆元 */
LL qpow(int a, int k, int p) {
	LL res = 1, t = a;
	while (k) {
		if (k & 1) res = res * t % p;
		t = t * t % p;
		k >>= 1;
	}
	return res;
}

/* 预处理阶乘的余数fact[]和阶乘逆元的余数infact[] */
fact[0] = infact[0] = 1;	// 0! = 1,1的任何逆元为1
for (int i = 1; i < N; i++) {	// 递推求解
	fact[i] = (LL)fact[i - 1] * i % MOD;
	infact[i] = (LL)infact[i - 1] * qpow(i, MOD - 2, MOD) % MOD;
}

/* C(a, b)的值 */
int C(int a, int b) {
	return fact[a] * infact[a - b] * infact[b] % MOD;
}

7.3 Lucas定理

Lucas定理:若 p p p 为质数,则对于任意整数 1 ≤ p ≤ m ≤ n 1≤p≤m≤n 1pmn ,有 C n m ≡ C n   m o d   p m   m o d   p C n / p m / p ( m o d p ) \text{C}_n^m \equiv \text{C}_{n \bmod p}^{m \bmod p}\text{C}_{n/p}^{m/p}\pmod p CnmCnmodpmmodpCn/pm/p(modp)

适用于较低数据量、 1 ≤ m ≤ n ≤ 1 0 18 , 1 ≤ p ≤ 1 0 5 1≤m≤n≤10^{18},1≤p≤10^5 1mn1018,1p105 的情况,时间复杂度: O ( log ⁡ p n ⋅ p log ⁡ p ) O(\log_p n\cdot p\log p) O(logpnplogp)

typedef long long LL;

/* 快速幂模板,用来求模为质数的逆元 */
LL qpow(int a, int k, int p) {
	LL res = 1, t = a;
	while (k) {
		if (k & 1) res = res * t % p;
		t = t * t % p;
		k >>= 1;
	}
	return res;
}

/* 求int型数的组合数C(a, b) */
int C(int a, int b, int p) {
	if (a < b) return 0;

	LL x = 1, y = 1;	// 由最初的定义式,x为分子,y为分母
	for (int i = a, j = 1; j <= b; i--, j++) {
		x = x * i % p;	// x = a! / (a-b)!
		y = y * j % p;	// y = b!
	}

	return x * qpow(y, p - 2, p) % p;	// 结果即为x * (y的逆元) mod p
}

/* 通过Lucas定理求long long型数的组合数lucas(a, b) */
int lucas(LL a, LL b, int p) {
	if (a < p && b < p) return C(a, b, p);	// a, b都小于p时用逆元法即可
	return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}

7.4 分解质因数法求组合数

不取模,求出组合数的真实值

  1. 筛法求出范围内的所有质数
  2. 通过公式 C n m = n ! ( n − m ) ! m ! \text{C}_n^m=\frac{n!}{(n-m)!m!} Cnm=(nm)!m!n! 求出每个质因子的次数。 n ! n! n! p p p 的次数是 n p + n p 2 + n p 3 + ⋯ \frac np + \frac n{p^2} + \frac n{p^3} + \cdots pn+p2n+p3n+
  3. 高精度乘法将所有质因子相乘
int n;
int primes[N], len;	// 存储所有质数
int sum[N];			// 存储每个质数的次数
bool st[N];			// 存储每个数是否已被筛掉

/* 线性筛法求素数 */
void get_primes(int n) {
	for (int i = 2; i <= n; i++) {
		if (!st[i]) primes[cnt++] = i;
		for (int j = 0; primes[j] <= n / i; j++) {
			st[primes[j] * i] = true;
			if (i % primes[j] == 0) break;
		}
	}
}

/* 求n!中质因子p的次数:n / p + n / p^2 + n / p^3 + ... */
int get(int n, int p) {
	int res = 0;
	while (n) {
		res += n / p;
		n /= p;
	}
	return res;
}

/* 高精度乘低精度模板 */
vector<int> mul(vector<int> &A, int b) {
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size() || t; i++) {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
  
    // if (t) C.push_back(t);
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

/* 预处理范围n以内所有质因子,存储每个质因子的个数 */
int a, b;	// 所求组合数C(a, b)的上下标

get_primes(a);
for (int i = 0; i < len; i++) {	// 求每个质因子的次数
	int p = primes[i];
	sum[i] = get(a, p) - get(a - b, p) - get(b, p);	// C(a, b) = a! / ((a - b)! * b!)
}

/* 用高精度乘法将所有质因子相乘,存于数组 */
vector<int> res;
res.push_back(1);	// 初值为1
for (int i = 0; i < len; i++)
	for (int j = 0; j < sum[i]; j++)	// 乘sum[i]次primes[i]
		res = mul(res, primes[i]);

7.5 卡特兰数

卡特兰数: C n = C 2 n n n + 1 C_n=\frac{\text{C}_{2n}^n}{n+1} Cn=n+1C2nn

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Akira37

💰unneeded

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值