1.组合数学的基本定义与基础题型
很水,可以选择性阅读。
例题:洛谷 P5135 painting
给定一个网格,每一列一个元素涂成黑色,求单调下降与单调不升的方案数,对 1 0 9 + 7 10^9+7 109+7 取模。
单调下降 :显然 C n m C _n^m Cnm
单调不升 :显然 C n + m – 1 m C_{n + m – 1}^m Cn+m–1m
考虑位置 a i + i a_i + i ai+i 是单调的,且在 [ 2 , n + m ] [ 2 , n + m ] [2,n+m] 区间内,选 m 种,共有 C n + m – 1 m C_{n + m – 1}^m Cn+m–1m 种选择方式。
#include<iostream>
#include<cstdio>
#define MAXN 1000005
using namespace std;
const int mod=1e9+7;
int inv[MAXN];
int C(long long n,long long m){
if(n<m) return 0;
int ans=1;
for(long long i=1;i<=m;i++)
ans=1ll*(n-i+1)%mod*inv[i]%mod*ans%mod;
return ans;
}
void solve(){
long long n,m,opt;
cin >> n >> m >> opt;
if(opt==1){
cout << C(n,m) << endl;
}
else{
cout << C(n+m-1,m) << endl;
}
}
int main(){
inv[1]=1;
for(int i=2;i<MAXN;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
int T; cin >> T;
while(T--) solve();
return 0;
}
例题:洛谷 P8432 「WHOI-2」ぽかぽかの星
一场有两道大模拟的公开赛的签到题。
求满足以下条件的长度为 n n n 的数列的数量,对 1 0 9 + 7 10^9+7 109+7 取模:
- 0 < a 1 ≤ a 2 ≤ a 3 ⋯ ≤ a n ≤ k 0<a_1\leq a_2\leq a_3\dots \leq a_n\leq k 0<a1≤a2≤a3⋯≤an≤k。
- ∀ i ≠ j , a i + a j ≠ k + 1 \forall i\not = j,a_i+a_j\not = k+1 ∀i=j,ai+aj=k+1。
分 k k k 奇偶性讨论,将两个不能同时出现的数放在一组。
k k k 为偶数时,有 k 2 \frac{k}{2} 2k 组,枚举选的组数,再计算对于所有组数的答案。
即:
∑ i = 1 k 2 C k 2 i C n − 1 i − 1 2 i \sum_{i = 1}^{\frac{k}{2}} C_{\frac{k}{2}}^iC_{n - 1}^{i - 1}2^i i=1∑2kC2kiCn−1i−12i
k k k 为奇数时,需要再计算 k + 1 2 \frac{k+1}{2} 2k+1 是否出现过的答案。
即:
∑ i = 1 k 2 C k 2 i C n − 1 i − 1 2 i + ∑ i = 1 k 2 C k 2 i C n − 2 i − 1 2 i \sum_{i = 1}^{\frac{k}{2}} {C_{\frac{k}{2}}^iC_{n - 1}^{i - 1}2^i} +\sum_{i = 1}^{\frac{k}{2}} C_{\frac{k}{2}}^iC_{n - 2}^{i - 1}2^i i=1∑2kC2kiCn−1i−12i+i=1∑2kC2kiCn−2i−12i
#include<iostream>
#include<cstdio>
#define MAXN 5000005
using namespace std;
const int mod=1e9+7;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int fac[MAXN],invfac[MAXN],base2[MAXN];
int C(int n,int m){
if(n<m) return 0;
return 1ll*fac[n]*invfac[m]%mod*invfac[n-m]%mod;
}
void solve(){
int n,k,ans=0;
cin >> n >> k;
if(n==1){
cout << k << endl;
return;
}
if(k%2==1){
for(int i=1;i<=k/2;i++){
ans=(ans+1ll*C(k/2,i)*C(n-1,i-1)%mod*base2[i]%mod)%mod;
ans=(ans+1ll*C(k/2,i)*C(n-2,i-1)%mod*base2[i]%mod)%mod;
}
cout << ans << endl;
}
else{
for(int i=1;i<=k/2;i++){
ans=(ans+1ll*C(k/2,i)*C(n-1,i-1)%mod*base2[i]%mod)%mod;
}
cout << ans << endl;
}
}
int main(){
fac[0]=base2[0]=1;
for(int i=1;i<MAXN;i++) fac[i]=1ll*fac[i-1]*i%mod,base2[i]=(2ll*base2[i-1])%mod;
invfac[MAXN-1]=Fp(fac[MAXN-1],mod-2);
for(int i=MAXN-2;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
int T; cin >> T;
while(T--) solve();
return 0;
}
例题:CF294C Shaass and Lights
有 n n n 盏灯,其中亮了 m m m 盏,现每次可点亮一盏与亮的灯相邻的灯,求将所有的灯都点亮的方案数。
考虑初始时亮的灯把序列分成了 m + 1 m + 1 m+1 段,我们在每次操作时,选择其中的一段点亮一盏灯。根据可重集排列的性质,有:
a n s = ( n − m ) ! ∏ i = 1 m + 1 l e n i ! ans = \frac{(n - m)!}{\prod_{i = 1}^{m + 1} len_i!} ans=∏i=1m+1leni!(n−m)!
其中 l e n i len_i leni 为分离出的第 i i i 个区间的长度。
然后再考虑对于除最左侧的段和最右侧的段以外,其余的段在每次的选择时都有两种方案,选择最左侧的一盏灯点亮,或者选择最右侧的一盏灯点亮,因此,共有 2 n − m − l e n 1 − l e n m + 1 2^{n - m - len_1 - len_{m + 1}} 2n−m−len1−lenm+1 种选择方式。
相乘即得最终答案。
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define MAXN 100005
using namespace std;
const int mod=1e9+7;
int Fp(int x,int tms){
int ret=1;
while(tms){
if(tms&1) ret=1ll*ret*x%mod;
x=1ll*x*x%mod; tms>>=1;
}
return ret;
}
int n,m,base2[MAXN],fac[MAXN],invfac[MAXN],a[MAXN];
bool flag[MAXN];
int main(){
ios :: sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m;
base2[0]=fac[0]=1;
for(int i=1;i<=m;i++) cin >> a[i];
stable_sort(a+1,a+m+1);
for(int i=1;i<=n;i++){
fac[i]=1ll*fac[i-1]*i%mod;
base2[i]=(base2[i-1]+base2[i-1])%mod;
}
invfac[n]=Fp(fac[n],mod-2);
for(int i=n-1;i>=0;i--) invfac[i]=1ll*invfac[i+1]*(i+1)%mod;
int ans=fac[n-m];
for(int i=1;i<=m;i++){
int tmp=a[i]-a[i-1]-1;
if(tmp==0) continue;
ans=1ll*ans*invfac[tmp]%mod;
if(i!=1) ans=1ll*ans*base2[tmp-1]%mod;
}
if(a[m]!=n) ans=1ll*ans*invfac[n-a[m]]%mod;
cout << ans << endl;
return 0;
}
2.容斥原理在组合数学中的思想及应用
例题:洛谷 P5339 [TJOI2019]唱、跳、rap和篮球
有 A A A 个人喜欢唱, B B B 个人喜欢跳, C C C 个人喜欢 rap , D D D 个人喜欢篮球,选 N N N 个人排成一列,如果连续的 4 4 4 个人分别喜欢唱,跳, rap ,篮球,则他们会讨论蔡徐坤,现需要求出没有人讨论蔡徐坤的排列方案数,有同样爱好的人被视为同样的。
N ≤ 1000 , A , B , C , D ≤ 500 N\leq1000,A,B,C,D\leq500 N≤1000,A,B,C,D≤500 。
定义 f ( n , a , b , c , d ) f(n,a,b,c,d) f(n,a,b,c,d) 为 a a a 个喜欢唱, b b b 个喜欢跳, c c c 个喜欢 rap , d d d 个喜欢篮球的人任意排列成 n n n 个人的方案数,对 998244353 998244353 998244353 取模。
这个东西显然等于
∑ i = 1 a ∑ j = 1 b ∑ k = 1 c ∑ l = 1 d [ i + j + k + l = n ] × n ! i ! j ! k ! l ! \sum_{i = 1}^{a}\sum_{j = 1}^{b}\sum_{k = 1}^{c}\sum_{l = 1}^{d}[i + j + k + l = n] \times \frac{n!}{i!j!k!l!} i=1∑aj=1∑bk=1∑cl=1∑d[i+j+k+l=n]×i!j!k!l!n!
把 n ! n! n! 提出来,里面很显然能 NTT , O ( n log n ) O(n\log n) O(nlogn) 能求。
之后考虑容斥,有:
a n s = ∑ i = 0 ⌊ n 4 ⌋ ( − 1 ) i C n − 3 i i × f ( N − 4 i , A − i , B − i , C − i , D − i ) ans = \sum_{i = 0}^{\lfloor\frac{n}{4}\rfloor}(-1)^i C_{n - 3i}^{i} \times f(N - 4i , A - i , B - i , C - i , D - i) ans=i=0∑⌊4n⌋(−1)iCn−3ii×f(N−4i,A−i,