YbtOJ 「数学基础」 第4章 组合数学
组合数
加法原理
分类计数原理 比如说做一件事有
乘法原理
分步计数原理 比如说做一件事分
排列数
从
计算公式:
全排列:
组合数
从
特别地 当
求组合数
递推组合数
时间复杂度
二项式定理
对于一个形如
A. 【例题1】计算系数
直接用二项式定理即可
code:
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define mid ((l+r)>>1)
#define int long long
const int N = 1e3 + 5;
const int mod = 10007;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int c[N][N];//i个中选择j个
int C ( int n , int m )//n个中选出来m个
{
for ( int i = 0 ; i <= n ; i ++ )
for ( int j = 0 ; j <= i ; j ++ )
{
if ( !j ) c[i][j] = 1;
else c[i][j] = ( c[i-1][j] + c[i-1][j-1] ) % mod;
}
return c[n][m] % mod;
}
int ksm ( int x , int k )
{
int base = x , ans = 1;
while (k)
{
if ( k & 1 ) ans = ans * base % mod;
base = base * base % mod;
k >>= 1;
}
return ans;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int a = read() % mod , b = read() % mod , k = read() , n = read() , m = read();
cout << C ( k , min ( n , m ) ) % mod * ksm ( a , n ) % mod * ksm ( b , m ) % mod << endl;
return 0;
}
B. 【例题2】方案统计
预处理阶乘逆元
我们可以知道
组合数
计算公式为
那么将计算公式下方的两个东西用逆元乘起来
int C ( int n , int m )
{
if ( n < m ) return 0;
return fac[n] % mod * inv[m] % mod * inv[n-m] % mod;
}
定理
int lucas ( int n , int m )
{
if ( m == 0 ) return 1;
return C ( n % mod , m % mod ) * lucas ( n / mod , m / mod ) % mod;
}
code:
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define mid ((l+r)>>1)
#define int long long
const int N = 2e6 + 5;
const int mod = 10007;
inl int read ()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
return x * f;
}
int fac[N] , inv[N];
void init ()
{
fac[0] = inv[0] = fac[1] = inv[1] = 1;
for ( int i = 2 ; i <= mod ; i ++ )
inv[i] = ( ( mod - mod / i ) * inv[mod%i] + mod ) % mod , fac[i] = fac[i-1] * i % mod;
for ( int i = 2 ; i <= mod ; i ++ ) // 求阶乘逆元
inv[i] = inv[i-1] * inv[i] % mod;
}
int C ( int n , int m )
{
if ( n < m ) return 0;
return fac[n] % mod * inv[m] % mod * inv[n-m] % mod;
}
int lucas ( int n , int m )
{
if ( m == 0 ) return 1;
return C ( n % mod , m % mod ) * lucas ( n / mod , m / mod ) % mod;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
init();
for ( int i = 1 ; i <= T ; i ++ )
{
int n = read() , m = read();
cout << lucas ( n , m ) << endl;
}
return 0;
}
C. 【例题3】古代猪文
题意
- 远古时期猪文文字总个数为
。 - 那个朝代流传的猪文文字恰好为远古时期的
,其中 是 的一个正约数(可以是 或 )。(枚举 约数) - 然而从
个文字中保留下 个的情况也是相当多的。(即组合数) - 所有可能的
的所有情况数加起来为 的话,那么他研究古代文字的代价将会是 。
可以得到题目真正需要求的柿子 (枚举
一个前置结论:
证明:首先由费马小定理得
则
我们设
所以
所以
即
由上面结论可以得出答案就是
即求:
我们如果对一坨柿子用
所以我们对于
进而可以得出四个同余方程:
所以我们首先对于四个模数分别求出
其次要找到一个
之后快速幂求
code:
#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define int long long
const int mod = 999911658;
const int N = 1e6 + 5;
const int b[5] = { 0 , 2 , 3 , 4679 , 35617 };
inl int read ()
{
int x = 0 , f = 1;
char ch = cin.get();
while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
return x * f;
}
int nn , G , res[5] , S , fac[N] , inv[N];
int ksm ( int x , int k , int p )
{
int base = x , ans = 1;
while (k)
{
if ( k & 1 ) ans = ans * base % p;
base = base * base % p;
k >>= 1;
}
return ans;
}
void init ( int p )
{
fac[0] = fac[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 n , int m , int p )
{
if ( m > n ) return 0;
return fac[n] % p * inv[m] % p * inv[n-m] % p;
}
int lucas ( int n , int m , int p )
{
if ( m == 0 ) return 1;
return C ( n % p , m % p , p ) * lucas ( n / p , m / p , p ) % p;
}
int invv ( int x , int p ) { return ksm ( x , p - 2 , p ); }
void CRT ()
{
for ( int i = 1 ; i <= 4 ; i ++ )
S = ( S + res[i] * ( mod / b[i] ) % mod * invv ( mod / b[i] , b[i] ) % mod ) % mod;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
nn = read() , G = read();
if ( G % ( mod + 1 ) == 0 ) { cout << 0 << endl; return 0; }
for ( int i = 1 ; i <= 4 ; i ++ )
{
init(b[i]);
for ( int k = 1 ; k * k <= nn ; k ++ )
if ( nn % k == 0 )
{
res[i] = ( res[i] + lucas ( nn , k , b[i] ) ) % b[i];
if ( k * k != nn ) res[i] = ( res[i] + lucas ( nn , nn / k , b[i] ) ) % b[i] ;
}
}
CRT();
cout << ksm ( G , S , mod + 1 ) << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】