YBTOJ 6.4组合数学

A.计算系数

image
image

二项式定理

我们小学的时候就知道杨辉三角可以用来求 \((a + b)^n\) 的展开系数
并且我们小学的时候就知道杨辉三角可以用来求组合数

然后我们创造性地把这俩结合起来

就有 \((a + b) ^n\)\(a^xb^{n - x}\) 项系数为 \(\text{C}_n^x\)

进一步地 我们考虑 \((ax + by)^ n\) 的展开系数
\(ax\)\(by\) 看作一个整体
那么第 \(x^my^{n - m}\) 项就是 \(\text C _n^m(a^m \times x^m) + \text C _n^m(b^{n - m} \times y^{n - m})\)

点击查看代码
#include <bits/stdc++.h>
#define int long long

using namespace std;

const int mod = 10007;
int a, b, k, n, m, ans;

int ksm(int x, int y) {
	int ret = 1;
	while (y) {
		if (y & 1) ret = ret * x % mod;
		x = x * x % mod;
		y >>= 1;
	}
	return ret;
}

int inv(int x) {
	return ksm(x, mod - 2);
}

int C(int x, int y) {
	int ret = 1;
	for (int i = x + 1; i <= y; ++i) ret = ret * i % mod;
	for (int i = 1; i <= y - x; ++i) ret = ret * inv(i) % mod;
	return ret;
}

signed main() {
	scanf("%lld%lld%lld%lld%lld", &a, &b, &k, &n, &m);
	
	ans = (ksm(a, n) * ksm(b, m)) % mod;
	ans = (ans * C(m, k)) % mod;

	printf("%lld",ans);
	
	return 0;
}

B.方案统计

image
image

Lucas 定理:\(C_n^m \mod p = C_{n \mod p}^{m \mod p} * C_{\left\lfloor\dfrac{n}{p}\right\rfloor} ^ {\left\lfloor\dfrac{m}{p}\right\rfloor}\)

后项递归下去即可

点击查看代码
#include <bits/stdc++.h>
#define ll long long

#define int long long

using namespace std;

const int N = 1e5 + 0721;
const int mod = 10007;
ll inv[N], fac[N];
int T;

void init() {
	fac[0] = inv[0] = 1;
	fac[1] = inv[1] = 1;
	for (int i = 2; i <= mod; ++i) {
		fac[i] = fac[i - 1] * i % mod;
		inv[i] = ((mod - mod / i) * inv[mod % i] + mod) % mod;
	}
	for (int i = 2; i <= mod; ++i) inv[i] = inv[i - 1] * inv[i] % mod;
}

inline ll C(ll n, ll m) {
	if (n < m) return 0;
	return fac[n] * inv[m] % mod * inv[n - m] % mod;
}

ll lucas(ll n, ll m) {
	if (m == 0) return 1;
	else return C(n % mod, m % mod) * lucas(n / mod, m / mod) % mod;
}

signed main() {
	scanf("%lld", &T);
	init();
	while (T--) {
		ll n, m;
		scanf("%lld%lld", &n, &m);
		printf("%lld\n",lucas(n, m));
	}
	return 0;
}

C.古代猪文

image
image

令人灵魂升华的数论全家桶

\(g^p \mod 999911659\)

首先利用费马小定理转化为 \(g^{p \mod 999911658} \mod 999911659\)

那我们把指数求出来然后上快速幂即可

考虑指数 \(p\)\(p = \sum\limits d | n \text{ } C_n^d\)

考虑暴力枚举因子 复杂度是够用的

关键在于计算组合数

模数太大 不能用 Lucas 定理计算

考虑将 \(999911658\) 质因数分解成 \(2 \times 3 \times 4679 \times 35617\)

分别求出 $p $ 在对这四个质因子取模时的结果

然后用 CRT 解同余方程组即可得到答案

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;


const int N = 50010;
const int mod = 999911658;
int fac[N], inv[N];
int a[5], b[5] = { 0, 2, 3, 4679, 35617 };
int nn, G, val;

int ksm(int x, int y, int p) {
	int ret = 1;
	while (y) {
		if (y & 1) ret = ret * x % p;
		x = x * x % p;
		y >>= 1;
	}
	return ret;
}

void init(int p) {
	fac[0] = fac[1] = 1;
	inv[0] = inv[1] = 1;
	for (int i = 2; i <= p; ++i) {
		fac[i] = fac[i - 1] * i % p;
		inv[i] = ((p - p / i) * inv[p % i] % p + p) %  p;
	}
	for (int i = 2; i <= p; ++i) inv[i] = inv[i - 1] * inv[i] % p;
}

int C(int m, int n, int p) {
	if (n < m) return 0;
	return fac[n] * inv[m] % p * inv[n - m] % p;
}

int lucas(int m, int n, int p) {
	if (n < m) return 0;
	if (m == 0) return 1;
	else return C(m % p, n % p, p) * lucas(m / p, n / p, p) % p;
}

void crt() {
	for (int i = 1; i <= 4; ++i) val = (val + a[i] * (mod / b[i]) % mod * ksm(mod / b[i], b[i] - 2, b[i])) % mod;
}

signed main() {
	scanf("%lld%lld", &nn, &G);
	if (G % (mod + 1) == 0) {
		printf("0");
		return 0;
	}
	
	for (int k = 1; k <= 4; ++k) {
		init(b[k]);
		for (int i = 1; i * i <= nn; ++i) {
			if (nn % i == 0) {
				a[k] = (a[k] + lucas(i, nn, b[k])) % b[k];
				if(i * i != nn) a[k] = (a[k] + lucas(nn / i, nn, b[k])) % b[k];
			}
		}
	}
	crt();
	
	printf("%lld",ksm(G, val, mod + 1));
	
	return 0;
}
posted @ 2023-09-08 14:22  Steven24  阅读(45)  评论(1编辑  收藏  举报