NOIP数学学习笔记 Sakura_xyz

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<a1a2a3ank
  • ∀ 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=12kC2kiCn1i12i
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=12kC2kiCn1i12i+i=12kC2kiCn2i12i

#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!(nm)!

其中 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}} 2nmlen1lenm+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 N1000,A,B,C,D500

定义 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=1aj=1bk=1cl=1d[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=04n(1)iCn3ii×f(N4i,Ai,

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值