首页 > 其他分享 >组合数学专题

组合数学专题

时间:2022-12-13 21:33:29浏览次数:67  
标签:专题 组合 int 题解 sum displaystyle 数学 binom ll

组合数学专题!

最近 noip 考完了,决定试试冲冲省选,虽然没什么希望。

无望的努力也是一种独特的体验吧。

之后如果可能,会写一个 OI 经历的博客,最近真的有点迷茫,先学再说。

1. 推式子

例 1.1

题意

DTOJ P1315 集合计数

一个有 \(N\) 个元素的集合有 \(2^N\) 个不同子集(包含空集),现在要在这 \(2^N\) 个集合中取出若干集合(至少一个),使得它们的交集的元素个数为 \(K\),求取法的方案数,答案模 \(1000000007\)。

题解

我的离谱思路里程

先选交集的 \(k\) 个元素,\(\displaystyle{\binom{n}{k}}\) 种方案。

剩下的元素有 \(n-k\) 个,包含选出的 \(k\) 个元素的集合数一共是 \(2^{n-k}\)

然后你需要选若干个集合,方案数 \(2^{2^{n-k}}\),因为要至少一个,所以 \(2^{2^{n-k}}-1\)

嗯嗯然后会多考虑交集元素个数大于 \(k\) 的情况

于是答案就是 \(\displaystyle{\binom{n}{k}(2^{2^{n-k}}-1)-\binom{n}{k+1}(2^{2^{n-k-1}}-1)}\)

代入发现是错的

为什么呢?因为 \(\displaystyle{\binom{n}{k}(2^{2^{n-k}}-1)}\) 中元素个数 \(k+1\) 的会被算 \(\displaystyle{\binom{k+1}{k}}\) 次,后面只减了一次,肯定是错的。

那我们这样不就完了吗? \(\displaystyle{\binom{n}{k}(2^{2^{n-k}}-1)-\binom{k+1}{k}\binom{n}{k+1}(2^{2^{n-k-1}}-1)}\)

还是错的 为什么呢?因为元素个数 \(k+2\) 的也会被多减!我们到这就看出来是 容斥 了。

用人类智慧猜一猜 \(\displaystyle{\sum_{k\le i\le n}(-1)^{i-k}\binom{i}{k}\binom{n}{i}(2^{2^{n-i}}-1)}\)

交上去是对的。交完我证了一下容斥,发现是对的(容斥只要证明后面的答案计算次数是 \(0\) )

(事实上,把 \(-1\) 去掉,式子改成 \(\displaystyle{\sum_{k\le i\le n}(-1)^{i-k}\binom{i}{k}\binom{n}{i}2^{2^{n-i}}}\) 也是对的)

正确的思路历程

设 \(F_i\) 表示 \(i\) 个元素的集合,选择若干个子集,他们的交集为空集的方案数,那我们的答案就是 \(\displaystyle{\binom{n}{k}F_{n-k}}\)

然后呢,我们发现 \(\displaystyle{\sum_{i=0}^n \binom{n}{i}F_{n-i}}\) 就是全部的方案数,即 \(2^{2^n}\)

于是 \(\displaystyle{\sum_{i=0}^n \binom{n}{i}F_{i}}=2^{2^n}\)

二项式反演即得上述式子

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int P = 1e9+7, N = 1e6+5;
int n,k;
int ksm(int a, int b, int p)
{
	int res=1;
	for(; b; b>>=1,a=(ll)a*a%p) if(b&1) res=(ll)a*res%p;
	return res;
}
int fc[N],fci[N];
int C(int n, int m)
{
	if(m<0 or n<0 or n<m) return 0;
	return (ll)fc[n]*fci[m]%P*fci[n-m]%P;
}
int main()
{
	scanf("%d%d",&n,&k);
	fc[0]=1;
	for(int i=1; i<=n; i++) fc[i]=(ll)fc[i-1]*i%P;
	fci[n]=ksm(fc[n],P-2,P);
	for(int i=n; i; i--) fci[i-1]=(ll)fci[i]*i%P;
	int sgn=1; ll ans=0;
	for(int i=k; i<=n; i++)
	{
		int res=(ll)C(n,i)*C(i,k)%P*(ksm(2,ksm(2,n-i,P-1),P)%P-1+P)%P;
		ans=(ans+res*sgn+P)%P; sgn=-sgn;
	}
	printf("%lld\n",ans);
	return 0;
}

二项式反演

\[g_n=\sum^n_{i=0}\binom{n}{i}f_i⇒f_n=\sum^n_{i=0}(−1)^{n−i}\binom{n}{i}g_i \]

我一直以为这东西很难,需要高端的组合技巧。

事实上它的难点是背式子!这东西其实非常好证(

首先你得会这两个式子

一些前置的组合数性质

1

\[\binom{n}{m}\binom{m}{k}=\binom{n}{k}\binom{n-k}{m-k} \]

左边:从 \(n\) 个数选 \(m\) 个,再从 \(m\) 个数选出 \(k\) 个

右边:从 \(n\) 个数选 \(k\) 个,再从剩下的 \(n-k\) 里选 \(m-k\) 个

这两个显然是等价的!(事实上大部分组合数性质都有组合解释)

2 二项式定理的推论

二项式定理

\[\sum_{i=0}^{n}\binom{n}{i}a^ib^{n-i}=(a+b)^n \]

证明自己搜,这是数学基础知识)

由这个我们有一些推论

\[\sum_{i=0}^{n}\binom{n}{i}(-1)^i=(-1+1)^n=[n=0]\\ \sum_{i=0}^{n}\binom{n}{i}=(1+1)^n=2^n \]

二项式反演的证明

你要证这个:

\[g_n=\sum^n_{i=0}\binom{n}{i}f_i⇒f_n=\sum^n_{i=0}(−1)^{n−i}\binom{n}{i}g_i \]

带进去不就好了:

\[\Leftarrow f_n=\sum^n_{i=0}(−1)^{n−i}\binom{n}{i}\sum^i_{j=0}\binom{i}{j}f_j \]

交换求和号:

\[\Leftarrow f_n=\sum^n_{j=0}f_j\sum^n_{i=j}(−1)^{n−i}\binom{n}{i}\binom{i}{j} \]

注意到右边的式子可以化(运用上述的两个性质):

\[\begin{align*} &\sum^n_{i=j}(−1)^{n−i}\binom{n}{i}\binom{i}{j}\\ =&\sum^n_{i=j}(−1)^{n−i}\binom{n}{j}\binom{n-j}{i-j}\\ =&\binom{n}{j}\sum^n_{i=j}(−1)^{n−i}\binom{n-j}{i-j}\\ =&\binom{n}{j}\sum^{n-j}_{i=0}(−1)^{n−i+j}\binom{n-j}{i}\\ =&\binom{n}{j}(−1)^{n+j}\sum^{n-j}_{i=0}(−1)^{i}\binom{n-j}{i}\\ =&\binom{n}{j}(−1)^{n+j}[n-j=0]\\ =&[n=j] \end{align*} \]

于是我们就要证:

\[\Leftarrow f_n=\sum^n_{j=0}f_j[n=j] \]

这显然 诶这不就证完了!

2 网格图路径计数

做了好多这种题,我们做个总结!反正类似卡塔兰数

多次容斥的套路题

就是两条线之间夹着,反复对称

例2.1.1

[JLOI2015]骗我呢 题解

把求最长不下降子序列数转成网格图路径计数

例2.1.2

DTOJ 5769 下棋 题解

例2.1.3

DTOJ 5932 Counting 题解

例 2.2 (未限制方向的方案数)

DTOJ 2537 J友 题解

多重集排列

\(\displaystyle\frac{n!}{m_1!m_2!\dots m_k!}\)

范德蒙德卷积

\(\displaystyle{\sum_i{\binom{n}{i}\binom{m}{k-i}}=\binom{n+m}{k}}\)

组合意义:从 \(n+m\) 选 \(k\) 个,可以枚举在 \(n\) 个中选择 \(i\) 个,则 \(m\) 个中就选择 \(k-i\) 个

常见套路——坐标系转化

\((x,y)\rightarrow (x+y,x-y)\)

\((0,\pm1)\rightarrow(\pm1,\mp1)\)

\((\pm1,0)\rightarrow(\pm1,\pm1)\)

例2.3

P2431. 棋盘路径(path)

\((0,0)\) 到 \((n,m)\) 的方案数,不能经过障碍点

题解

设 \(f_i\) 表示第一次到达的是 \(i\) 号障碍点。

转移显然

代码

#include <bits/stdc++.h>
#define ll long long
#define x first 
#define y second
using namespace std;
const ll N = 2e5+5;
const ll M = 3e3+5;
const ll mod = 1e9+7;
typedef pair<ll,ll> P;
ll n,m,k,mn;
ll fc[N],fci[N];
ll f[N];
P h[N];
ll qpw(ll a, ll b)
{
	ll ans=1;
	for(;b;b>>=1)
	{
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
    }
    return ans;
}
void prework()
{
	fc[0]=1;
    for(ll i=1; i<=mn; i++)
    	fc[i]=fc[i-1]*i%mod;
    fci[mn]=qpw(fc[mn],mod-2);
    for(ll i=mn-1; i>=0; i--)
	    fci[i]=fci[i+1]*(i+1)%mod;
}
inline ll C(ll yy, ll xx) {return fc[yy]*fci[xx]%mod*fci[yy-xx]%mod; }
int main()
{
	scanf("%lld%lld%lld",&n,&m,&k); mn=n+m;
	for(ll i=1; i<=k; i++) scanf("%lld%lld",&h[i].x,&h[i].y);
	sort(h+1,h+1+k);
	h[++k]=make_pair(n,m);
	prework();
	for(ll i=1; i<=k; i++)
	{
		f[i]=C(h[i].x+h[i].y,h[i].x);
	    for(ll j=1; j<i; j++)
	    	f[i]=(f[i]-f[j]*C(h[i].x+h[i].y-h[j].x-h[j].y,h[i].x-h[j].x)%mod+mod)%mod;
	}
	printf("%lld\n",f[k]);
	return 0;
}

标签:专题,组合,int,题解,sum,displaystyle,数学,binom,ll
From: https://www.cnblogs.com/copper-carbonate/p/16980684.html

相关文章