YBTOJ 6.4组合数学
A.计算系数
二项式定理
我们小学的时候就知道杨辉三角可以用来求 \((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.方案统计
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.古代猪文
令人灵魂升华的数论全家桶
求 \(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;
}