『组合数学总结1:基础组合数学和组合原理』

Parsnip·2020-03-18 16:44·4266 次阅读

『组合数学总结1:基础组合数学和组合原理』

Preface#

前排提示:本文数学公式较多,加载LATEX需要一定时间,可能会导致浏览器暂时卡顿,请耐心等待数学公式正常显示.

组合数学知识点的总结,本来准备写在一起的,结果发现字数有点多,导致markdown编辑器频繁卡顿,那就分三篇发布好了.

Update:目前第一篇就是组合基础和组合原理,预计第二篇基础高数,生成函数和特殊计数数列,第三篇多项式算法,至于会不会咕咕咕那就不知道了.

基本组合数学#

一些定义和符号#

1.n!表示n的阶乘,n!=1×2××n,用nk_表示下降阶乘幂,简称下降幂,nk_=n×(n1)××(nk+1),用nk¯表示上升阶乘幂,简称上升幂,nk¯=n×(n+1)××(n+k1).

2.Anm表示从n个元素种选出m个组成的排列数量,称为排列数,Anm=n!(nm)!.

3.Cnm(nm)表示从n个元素种选出m个组成的子集数量,称为组合数,Cnm=(nm)=n!m!(nm)!.

4. 下降阶乘幂nk_n可以是任意实数,当我们将组合数(rk)定义改为(rk)=rk_k!时,称其为二项式系数,其中上指标可以为任意实数,下指标仍为整数,当下指标k<0时,二项式系数的值为0.

计数原理和计数方法#

计数原理#

1. 加法原理:如果完成一件事情有n类方法,且第i类方法有ai种方案,则完成这件事情共有ai种方案。

2. 乘法原理:如果完成一件事情有n个步骤,且第i个步骤有ai种方案,则完成这件事情共有ai种方案。

计数方法#

1. 相邻元素捆绑法

例:现有7人站成一排,其中甲乙相邻且丙丁相邻,共有多少种不同的排法?

将甲乙两元素捆绑成整体并看成一个复合元素,同时丙丁也看成一个复合元素,再与其它元素进行排列. 因此方案数为A55×A22×A22=480种.

2. 不相邻元素插空法

例:现有4A球,2B球,3C球,把它们排成一排,所有A球不能相邻,且所有的球都是可分辨的,求有多少种排法?

先把B,C球排好,此时出现了6个间隙,把A球插入这些间隙即可. 因此方案数为A55×A64=43200种.

3. 相同元素隔板法

例:把10个完全相同的球放入7个盒子中,每个盒子至少放一个,求有多少种方案?

因为球是相同的,所以可以将球先随意地排成一行,此时球与球之间出现了9个间隙,插入6个板就可以做到把球分割为7个非空部分的效果. 因此方案数为(96)=84种.

重要的组合恒等式#

对称恒等式#

(nk)=(nnk)

对于n,kZ,n0成立.

证明:根据定义阶乘展开即可.

吸收/提取恒等式#

(rk)=rk(r1k1)

对于rR,kZk0成立.

证明:(rk)=rk_k!=rk×(r1)k1_(k1)!=rk(r1k1)

加法/归纳恒等式#

(rk)=(r1k)+(r1k1)

对于rR,kZ成立.

证明:

(r1k1)+(r1k)=(r1)k1_(k1)!+(r1)k_k! =k(r1)k1_k!+(rk)(r1)k1_k!=rk_k!=(rk)

相伴恒等式#

(rk)(rk)=r(r1k),k(rk)=(rk+1)(rk1)

对于rR,kZ成立.

证明:

(rk)(rk)=r!(rk1)!k!=r×(r1)rk1_(rk1)! =r(r1rk1)=r(r1k) k(rk)=rk_(k1)!=(rk+1)rk1_(k1)!=(rk+1)(rk1)

上指标反转#

(rk)=(1)k(rk1k)

对于rR,kZ成立.

证明:

(rk)=rk_k! =r×(r1)×...×(rk+1)k! =(1)k×(kr1)×(kr2)×...×(r)k! =(1)k(rk1)k_k!=(1)k(rk1k)

三项式系数恒等式#

(rm)(mk)=(rk)(rkmk)

对于rR,m,kZ成立.

证明:

(rm)(mk)=r!(rm)!(mk)!k!=(rkmk)(rk)

上指标求和#

i=mn(im)=(n+1m+1)

对于n,mZ成立.

证明:

设有n+1个物品,标号为1n+1,现在从中选取m+1个物品的方案数即为(n+1m+1),同时等价于在n个物品每个物品后插入一个结束符,强选结束符并在结束符前选m个物品的方案数。

平行恒等式#

i=0n(m+ii)=(m+n+1n)

对于n,mZ成立。

证明:

i=0n(m+ii)=i=0n(m+im) =i=mn+m(im)=(n+m+1m+1)=(n+m+1n)

范德蒙德卷积/下指标卷积#

k=0n(rk)(snk)=(r+sn)

对于r,sR,nZ成立。

证明:

左边表示从r个男生中选k个人,从s个女生中选出nk个人的方案数,求和即为在r+s个人中选n个人的方案数,可知:k=0n(rk)(snk)=(r+sn)

下指标点积#

i=0m(ni)(mi)=(n+mm)

假设m<n,则对于n,mZ成立.

证明:

i=0m(ni)(mi)=i=0m(ni)(mmi)=(n+mm)

上指标卷积#

i=0n(ia)(nib)=(n+1a+b+1)

对于n,a,bZ成立.

证明:考虑左式的组合意义为将n个物品分为在左右两部分,左边选a个,右边选b个的方案数,同时等价于插入一个分隔符,共n+1个物品选a+b+1个的方案数.

  • 上指标求和和上指标卷积同样存在非组合意义的直接证明方式,而范德蒙德卷积存在生成函数的证明方式,这保证了它们的绝对正确性.

二项式定理相关#

二项式定理#

根据二项式定理,组合数可以作为二项式高次幂展开式的系数:

(x+y)n=i=0n(ni)xiyni

该式被称作二项式定理.

证明:

根据数学归纳法,该式在n=1时显然成立,那么假设其在n=p1(p2)时也成立,则有:

(x+y)p=(x+y)p1(x+y)=(x+y)i=0p1(p1i)xiyp1i =i=0p1(p1i)xi+1yp1i+i=0p1(p1i)xiypi =i=1p(p1i1)xiypi+i=0p1(p1i)xiypi =i=0p((p1i1)+(p1i))xiypi=i=0p(pi)xiypi

可知对于n=p,该式也成立,故对于所有nN+,定理都成立.

二项式定理的推广#

由于二项式系数的上指标可以是任意实数,所以二项式定理也有推广形式:

(x+y)α=i=0(αi)xiyαi

其中αR,称为广义二项式定理.

  • 事实上,我们还需考虑右式的收敛性,不过在应用中完全不需要理会这点.

对于阶乘幂,二项式定理仍成立:

(x+y)n_=i=0n(ni)xi_yni_ (x+y)n¯=i=0n(ni)xi¯yni¯

证明略.

二项式定理的直接推论#

1.i=0n(ni)=(1+1)n=2n

2.i=0n(ni)[2|i]=i=1n(ni)[2|i]=2n1

证明:

首先n个数中选奇数个数和选偶数的数的方案肯定是一样的.那么考虑选偶数个数的方案:单独考虑最后一个数,若选,则还需在n1个数中选奇数个数,若不选,则还需选偶数个数,问题转回到推论一,答案即为2n1.

3.i=0n(1)i(ni)=(11)n=[n=0]

多项式定理#

定义多项式系数(n1+n2++nkn1,n2,,nk)=(n1+n2++nk)!n1!n2!nk!

则有如下的多项式定理成立:

(i=1kxi)n=i=1kni=n(n1+n2++nkn1,n2,,nk)i=1kxini

证明可以直接从组合角度考虑.

模域组合数的计算#

预处理杨辉三角的方法#

杨辉三角是二项式系数在三角形中的一种几何排列,由中国数学家杨辉提出. 形式化地,杨辉三角可以表示成如下的下三角矩阵:

[(00)000(10)(11)00(20)(21)(22)0(30)(31)(32)(33)]

其中Ai,j表示杨辉三角的第i行第j个数,有Ai,j=(ij). 杨辉三角满足Ai,j=Ai1,j+Ai1,j1,本质上是组合数的加法恒等式.

那么我们可以根据加法恒等式预处理一张n2大小的杨辉三角形的表,O(1)回答组合数。

对模数没有任何要求,时间复杂度O(n2)O(1).

Copy
#include <bits/stdc++.h> using namespace std; const int N = 5020; int C[N][N],n,Mod; inline int inc(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; } inline void Init(void) { C[0][0] = C[1][0] = C[1][1] = 1; for (int i = 2; i <= n; i++) { C[i][0] = C[i][i] = 1; for (int j = 1; j < i; j++) C[i][j] = inc( C[i-1][j] , C[i-1][j-1] ); } }

预处理阶乘的方法#

计算模意义下的组合式最常用的方法就是根据组合数的阶乘展开式计算。我们可以O(n)预处理阶乘,并用费马小定理求出最大数值阶乘的逆元,倒序更新所有阶乘的逆元,就可以实现O(1)回答组合数.

此时要求模数p为大于n,m的质数,换言之,1max(n,m)的阶乘在模p意义下均存在逆元时,该方法有效,时间复杂度O(n)O(1).

Copy
#include <bits/stdc++.h> using namespace std; const int Mod = 1e9 + 7 , N = 1e6 + 20; inline int inc(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; } inline int dec(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; } inline int mul(int a,int b) { return 1LL * a * b % Mod; } inline void Inc(int &a,int b) { return void( a = inc( a , b ) ); } inline void Dec(int &a,int b) { return void( a = dec( a , b ) ); } inline void Mul(int &a,int b) { return void( a = mul( a , b ) ); } inline int fastpow(int a,int b) { int res = 1; for (; b; Mul(a,a),b>>=1) if (b) Mul(res,a); return res; } inline int Inv(int x) { return fastpow( x , Mod - 2 ); } int inv[N],fac[N]; inline void Init(void) { inv[0] = fac[0] = 1; for (int i = 1; i <= N - 20; i++) fac[i] = mul( fac[i-1] , i ); inv[ N - 20 ] = Inv( fac[ N - 20 ] ); for (int i = N - 21; i >= 1; i--) inv[i] = mul( inv[i+1] , i+1 ); } inline int C(int n,int m) { return ( m > n || n < 0 || m < 0 ) ? 0 : mul( fac[n] , mul( inv[m] , inv[n-m] ) ); }

Lucas定理#

当模数pmax(m,nm)时,组合数分母的m!(nm)!就不存在逆元,此时我们需要用到Lucas定理:

(nm)(nmodpmmodp)×(npmp)(modp)

证明:

引理:(1+x)p1+xp(modp),pPrime.

有费马小定理可知(1+x)p1+x,又因为xxp,可知(1+x)p1+xp.

(1+x)n(1+x)npp(1+x)nmodp(modp) (1+x)n(1+xp)np(1+x)nmodp(modp)

用二项式定理展开上式中的三个二项式的幂:

i=0n(ni)xij=0np(npj)(xp)j×k=0nmodp(nmodpk)xk

左式取到xm项系数为(nm),右式取到xm项系数当且仅当j=mp,k=mmodp,对应项系数同余,所以就有:

(nm)(nmodpmmodp)×(npmp)(modp)

  • 另一种理解,Lucas定理的含义即为将组合数拆解为n,m按照p进制分解后每一位的组合数之积

回到上文,对于模数p较小时,只要p为质数,我们就可以运用Lucas定理递归求解组合数,时间复杂度O(p)O(logpn).

Copy
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 20; int inv[N],fac[N],n,m,Mod,T; inline int inc(int a,int b) { return a + b >= Mod ? a + b - Mod : a + b; } inline int dec(int a,int b) { return a - b < 0 ? a - b + Mod : a - b; } inline int mul(int a,int b) { return 1LL * a * b % Mod; } inline void Inc(int &a,int b) { return void( a = inc( a , b ) ); } inline void Dec(int &a,int b) { return void( a = dec( a , b ) ); } inline void Mul(int &a,int b) { return void( a = mul( a , b ) ); } inline int fastpow(int a,int b) { int res = 1; for (; b; Mul(a,a),b>>=1) if (b) Mul(res,a); return res; } inline int Inv(int x) { return fastpow( x , Mod - 2 ); } inline void Init(void) { inv[0] = fac[0] = 1; for (int i = 1; i <= Mod - 1; i++) fac[i] = mul( fac[i-1] , i ); inv[Mod-1] = Inv( fac[Mod-1] ); for (int i = Mod - 2; i >= 1; i--) inv[i] = mul( inv[i+1] , i+1 ); } inline int C(int n,int m) { return ( m > n || n < 0 || m < 0 ) ? 0 : mul( fac[n] , mul( inv[m] , inv[n-m] ) ); } inline int Lucas(int n,int m) { return m == 0 ? 1 : mul( C(n%Mod,m%Mod) , Lucas(n/Mod,m/Mod) ); } int main(void) { scanf( "%d" , &T ); while ( T --> 0 ) scanf( "%d%d%d" , &n , &m , &Mod ), Init() , printf( "%d\n" , Lucas(n+m,n) ); return 0; }

常见的组合问题#

组合数的单调性#

对于上指标确定的组合数,我们可以令f(x)=(nx)(nN+),则函数f是单峰函数,其极值在x=n2处取到.

组合数函数图像拟合.png

图:用连续函数的图像近似离散函数的图像

阶乘幂反转#

两类阶乘幂可以提取因数1进行互相转换:

xn_=(1)n(xn+1)n_,xn¯=(1)n(1xn)n¯

循环排列#

n个元素中选出m个排成圆圈的方案数,相当于线性排列时固定第一个数的方案。

一个循环排列可以对应m个线性排列,进而可以得到循环排列的计算公式:

Cirnm=Anmm=n!m×(nm)!

组合数的数论性质#

p为质数,则对于n[1,p1],有p | (pn)

证明:

(pn)=p×(p1)×...×(pn+1)n!Z n! | p×(p1)×...×(pn+1) (p,n)=1 n! | (p1)×(p1)×...×(pn+1) p | (pn)

不定方程的解数问题#

1. 正整数解

求不定方程x1+x2++xk=n的正整数解的个数。

这个问题等价于把n个球放入k个盒子中,每个盒子中至少有1个球,由隔板法可知其方案数为(n1k1)

2. 非负整数解

求不定方程x1+x2++xk=n的非负整数解的个数。

我们可以新增k个球,这样问题就等价于把n+k个球放入k个盒子中,每个盒子中至少有1个球,由隔板法可知其方案数为(n+k1k1)

3. 下界限制

求不定方程x1+x2++xk=n的整数解的个数,满足x1a1,x2a2,,xkak

这个问题等价于不定方程x1+x2++xk=na1a2ak的非负整数解个数,可以其方案数为(n+k1i=1naik1)

4. 上下界限制

求不定方程x1+x2++xk=n的整数解的个数,满足a1x1b1,a1x2b2,,akxkbk

首先把限制转换为0x1b1a1,,0xkbkak,运用容斥原理,答案即为:

S{1,2,,k}(1)|S|(n1xS(bxax)k1)

多重集排列数#

多重集指含有重复元素的广义集合。设多重集S={n1×a1,n2×a2,,nk×ak}是由n1a1n2a2...nkak组成的集合,则S的全排列个数为

(i=1kni)!i=1k(ni!)

证明:

对于朴素全排列,显然有(i=1kn)!种方案,而在多重集的排列过程中,每个ai出现了ni次,在这ni个位置间各个ai可以互相调换位置,有ni!中方案,故除去每一个ni可以调换位置的重复方案即为总排列数.

多重集的组合数#

设多重集S={n1×a1,n2×a2,,nk×ak}是由n1a1n2a2...nkak组成的集合,从中取出r(ri=1kni)个元素可以组成不同集合个数为:

(k+r1k1)i=1n(k+rni2k1)+1ijn(k+rninj3k1)+(1)k(k+ri=1knik1k1)

证明:

首先考虑rmin{ni}的情况,根据不定方程的非负整数解数量,可知答案为(k+r1k1).

然后可以考虑容斥原理,对于i,让选择ai个数超出ni成为一个性质,那么不具备任何性质的方案数即为上式.

高阶差分#

定义离散函数的差分算子(Δa)i=aiai1,则有:

(Δna)i=j=0n(1)j(nj)aij

证明:

容易在算子间定义四则运算,此处不再赘述.

定义平移算子(Ea)i=ai1,恒等算子(Ia)i=ai,那么就有Δ=IE.

根据二项式定理展开:

Δn=(IE)n=(1)nj=0n(nj)Ej(I)nj

取第i项就可以得到:

(Δna)i=(1)nj=0n(1)nj(nj)aij=j=0n(1)j(nj)aij

可以用多项式卷积算法求高阶差分序列的前n项.

下指标求和(HDU6333)#

对于组合数的下指标求和,即:

i=0m(ni)

没有很好的封闭形式解,但是对于多组(n,m)的询问,我们可以将n,m看作一个区间的左右端点,使用O(max{ni,mi}q)的莫队算法来求解.

考虑转移,对于mm+1,只需加上一个组合数(nm+1)即可. 对于nn+1,我们可以考虑用加法公式进行拆解:

i=0m(n+1i)=i=0m((ni)+(ni1))=i=0m(ni)+i=0m1(ni) =2i=0m(ni)(nm)

于是区间移动可以O(1)处理了.

自然数幂和#

自然数幂和指的是这一类求和问题:

Sk(n)=i=1nik

对于k比较小的情况,可以用组合恒等式推出通项公式.

k=1,S1(n)=n×(n+1)2

k=2,S2(n)=i=1ni+2i=1n(i2)=n×(n+1)×(2n+1)6

k=3,S3(n)=i=1ni+6i=1n(i+13)=n2×(n+1)24

  • 不难发现,k次多项式的求和式都可以表示为k+1次多项式.

对于k任意的情况,我们很难直接推出通项公式,但是可以通过多项式插值技巧求出答案,我们会在下文讨论这个问题.

组合原理#

鸽巢原理#

普通鸽巢原理#

鸽巢原理又称抽屉原理,形式化表述如下:

  • q1,q2,q3,,qn是正整数,如果将qin+1个物品放入n个盒子中,记第i个盒子里放了ai个物品,则总存在一个盒子j,满足ajqj.

证明:

反证法,若不存在这样的盒子j,则对于任意的i都有ai<qi,那么ai(qi1)<qin+1,与总球数为qin+1矛盾.

应用1:每十三个人中,总存在两人出生在同一个月.

证明:显然.

应用2:对于序列a1n,总存在区间[l,r]使得n|i=lrai .

证明:记其前缀和为数组s0n,共有n+1个元素,所以必定存在i,j满足sisj(modn),于是令l=i1,r=j即可.

平均值原理#

对于n个变量x1,x2,,xn,总满足:

mini=1n{ai}i=1nain , maxi=1n{ai}i=1nain

证明:反证法,导出矛盾.

Ramsey定理#

对于正整数n,m,总存在确定的上界x,满足对Kx中所有边随意黑白染色,其中要么包含黑色Kn,要么包含白色Km. 可以记x=R(n,m),称为Ramsey数.

证明略.

实例:R(3,3)=6. 可以考虑第一个点的五条边,根据鸽巢原理,总存在三条边同色,那么三条同色边连接的三个点要么构成同色K3,要么与第一个点构成另一种同色K3.

Ramsey定理在高维情况下仍然成立,即:

R(a1,a2,,an)<

反演原理#

反演的实质#

对于形如下的式子,我们称左右两式互为反演式:

(0)fi=j=1iAi,jgjgi=j=1iBi,jfj

我们可以把f,g看作只有一行的矩阵,于是反演原理本质上就是矩阵乘法求逆:

f=g×Ag=f×B

  • 我们还可以发现A,B都是下三角矩阵.

不过矩阵乘法并没有什么用,我们可以根据反演式倒推反演成立的充要条件. 方法其实很简单,我们可以将右式的表达式带入到左式里面,如果最后化简得到了恒等式,那就说明右式可以推导得到左式.

同理,我们再把左式带到右式里面,然后化简得到恒等式,才能说明反演成立.

那么我们来实操一下:

fi=j=1iAi,jk=1jBj,kfk fi=k=1ifk(j=kiAi,jBj,k)=k=1ifk[k=i]

于是就有了右边推左边的条件:

(1)j=kiAi,jBj,k=[k=i]

同理把左边代入右边:

gi=j=1iBi,jk=1jAj,kgk gi=k=1igk(j=kiBi,jAj,k)=k=1igk[k=i]

那么左边推右边的条件就是:

(2)j=kiBi,jAj,k=[k=i]

真正的反演原理:(1)(2)两式同时成立,则有反演式(0)成立.

对称矩阵的反演#

fi=j=1iAi,jgjgi=j=1iBi,jfj fi=j=inAj,igjgi=j=inBj,ifj

推导可知,上述两个反演式的代数充要条件均为:

j=kiAi,jBj,k=[k=i]j=kiAi,jBj,k=[k=i]

于是两个反演式等价.

反演系数转移#

fi=j=1isjAi,jgjgi=j=1iBi,jfj

对于形如上式的带权反演,我们可以合并系数,将权值转移到右式.

fi=j=1iAi,jGjGisi=j=1iBi,jfj fi=j=1iAi,jGjGi=j=1isiBi,jfj

常规等价反演#

根据对称矩阵反演和反演系数转移,可以得知以下四种反演是等价的反演:

fi=j=1i(1)jAi,jgjgi=j=1i(1)jBi,jfj fi=j=1iAi,jgjgi=j=1i(1)i+jBi,jfj fi=j=in(1)jAj,igjgi=j=in(1)jBj,ifj fi=j=inAj,igjgi=j=in(1)i+jBj,ifj

  • 当然反演可以是乘法形式的,仍然存在类似上式的等价形式.

容斥原理#

补集转化#

补集转化又称为减法原理,若集合S被划分成了两部分A,A¯,则A对象的数目为|A|=|S||A¯|.

基本容斥原理#

我们假设有全集S,以及n个集合,每个集合Ai中的元素具有性质Pi.

现在我们要求不具有任何性质的集合大小,则有如下的计算公式:

|i=1nAi¯|=|S|+T{1,2,...,n},T(1)|T||iTAi|

移项一下就可以得到另一种等价形式:

|i=1nAi|=T{1,2,...,n}(1)|T|1|iTAi|

两种形式的容斥原理都具有组合意义:

形式1:不具备任何性质的元素个数 = 元素总个数 至少具备一个性质的元素个数之和 + 至少具备两个性质的元素个数之和 至少具备三个性质的元素个数之和 ...

形式2:所有集合的并集大小 = 所有集合的大小之和 每两个集合之间的交集大小 + 每三个集合之间的交集大小 ...

以下提供一种形式2的证明:

考虑元素x被包含在k个集合中,则它在|T|=ttk的时候被计算了(kt)次.

设其总共被计算了P次,构造f(x)=(1x)k,根据二项式定理展开得到:

f(x)=i=0k(ki)xki(1)i

P=(k1)(k2)+(k3)+(1)k1(kk)

观察发现f(1)=1P,于是得出P=1.

应用1:欧拉函数φ(n)的表达式为n×(11pi).

证明:设Si代表质因数pi1n内所有倍数代表的集合,则φ(n)=n|i=1kSi|,应用容斥原理,可得φ(n)=nTP(1)|T|1niTpi,提取n,因式分解即可.

应用2:错位排列Dn的通项公式为n!+i=1n(1)i(ni)(ni)!.

证明:令一个数字排在其原本的位置代表方案具有一个性质,套用容斥原理形式一即可.

应用3:第二类斯特林数{nm}的表达式为1m!i=0m(1)i(mi)(mi)n.

证明:令一个盒子为空代表具有一个性质,套用容斥原理形式一,除掉盒子标号即可.

应用4:多重集组合数.

证明:公式在常见的组合问题一节中已经给出,把超额使用一类元素看组一个性质,用隔板法计算答案,套用容斥原理形式一即可.

广义容斥原理#

fg为定义在指标集上的函数,则有如下等价关系:

f(S)=TSg(T)g(S)=TS(1)|S||T|f(T)

证明:

首先右式代入左式,交换求和号:

f(S)=PSf(P)PTS(1)|T||P| =PSf(P)i=0|S||P|(1)i(|S||P|i) =PSf(P)[|S|=|P|]=f(S)

最后一步使用了二项式定理的推论3.

右式代入左式的证明过程相似,故定理成立.

快速Mobius变换#

在广义容斥原理的式子中,我们可能需要求解一个数组的高位前缀和,也就是子集和,直接枚举子集是O(3n)的,存在时间复杂度更低的算法.

我们可以考虑对一个集合S的子集贡献从高位到低位进行分类:首先是不包含最高位1子集的贡献,这其实是一个子问题;其次,包含最高位1的子集贡献我们再分为两类,不包含次高位1的子集贡献,这又是一个子问题;剩下同时包含最高位和次高位1的子集我们再类似地向下分类.

FMT贡献图.png

我们可以画出如上的贡献图. 以111为例,我们想知道它的子集和,首先可以考虑它的最高位1要不要,如果不要,那么可以直接把011的子集和累加给它,也就是最后一个粉丝箭头. 接着考虑要的情况,我们再看次高位的1要不要,如果不要,那么可以直接把101的子集和累加给它. 同样,最高两位都要的话,可以假设第三位不要,然后把110的子集和累加给它. 最后就是三位全都要了,显然他自己对自己也是有贡献的.

当然我们想要把011101110的子集和累加给111时,首先得保证它们的子集和已经求好了. 此时最好的方式就是直接按照图中箭头从上到下的顺序一层一层的枚举累加,这样就可以保证跨层询问子集和的时候一定已经完成累加了.

此时求和算法的时间复杂度类似于分治,有T(n)=2T(n2)+n2. 容易得知T(n)=O(nlog2n)n=2m时,时间复杂度就可以写成O(m×2m)m是元素个数.

还原算法我们称之为逆Mobius变换,只需把贡献扣掉就好了.

Copy
// t = 1 --> FMT , t = -1 --> IFMT inline void FMT(int *a, int n, int t) { for (int i = 1; i < 1 << n; i <<= 1) // i : the length of contribution arrow for (int p = i << 1 , j = 0; j < 1 << n; j += p) // p : the length of section , j : the number of position for (int k = 0; k < i; k++) a[i+j+k] += t * a[j+k]; // k : go through the section and add contribution }

二项式反演#

f(S)=F(|S|)g(S)=G(|S|),套用广义容斥原理:

F(|S|)=TSG(|S|)G(|S|)=TS(1)|S||T|F(|T|) F(|S|)=i=0|S|(|S|i)G(i)G(|S|)=i=0|S|(1)|S|i(|S|i)F(i)

可以把|S||T|直接看成整数,得到:

f(x)=i=0x(xi)g(i)g(x)=i=0x(1)xi(xi)f(i)

我们称这个反演式为二项式反演.

根据反演原理,我们还可以得到另外三种等价形式,其中最常用的是这种:

f(x)=i=xn(ix)g(i)g(x)=i=xn(1)xi(ix)f(i)

二项式反演的重要运用是至少和恰好型方案数的转换.

首先,我们设α(k)代表所有至少满足k的性质的方案数之和.

α(k)=T{1,2,...,n},|T|=k|iTAi|

我们发现α(k)将具有p(pk)个性质的元素计算了(pk)次.

假设β(k)代表恰好具有k个元素的方案数,则有递推公式如下:

β(k)=α(k)i=k+1n(ik)β(i) α(k)=i=kn(ik)β(i)

二项式反演,可以得到β(k)的计算式:

β(k)=i=kn(1)ik(ik)α(i)

应用1:推导错位排列Dn的通项公式.

fi代表恰好i个位置错排的方案数,则可以进行二项式反演:

n!=i=0n(ni)fi

应用2:推导第二类斯特林数{nm}的表达式

假设盒子有标号,设fi代表恰好i个盒子非空的方案数,则可以二项式反演,答案除掉盒子标号即可:

mn=i=0m(mi)fi

应用3:球染色问题:有n个球排成一行,你有k种颜色,要求给每一个球染色,相邻两个球颜色不可以相同,并且每种颜色至少使用一次,求方案数.

假设fi代表恰好使用i种颜色的方案数,则可以二项式反演:

k(k1)n1=i=0k(ki)fi

Min-Max容斥#

首先我们把一个整数n看成一个集合S(n)={1,2,,n},那么:

mini=1n{ai}=|i=1nS(ai)| , maxi=1n{ai}=|i=1nS(ai)|

可以用容斥原理改写max的表达式:

maxi=1n{ai}=|i=1nS(ai)|=T{1,2,,n}(1)|T|1|iTS(ai)|

于是就可以得到:

maxi=1n{ai}=T{1,2,,n}(1)|T|1miniT{ai}

minmax反过来该式也成立,在期望意义下该式也成立.

Kth Min-Max容斥#

我们可以套用一般minmax容斥的式子猜想一个k大值容斥的形式:

Kthmaxi=1n{ai}=T{1,2,,n}f(|T|)miniT{ai}

其中f(|T|)是容斥系数,具体是多少还不知道.

x+1大的元素显然会被统计到i=0x(xi)f(i+1)次,我们可以直接令[x+1=k]=i=1x(xi)f(i+1),于是就可以二项式反演,得到:

f(x+1)=i=0x(1)xi(xi)[i+1=k]

化简得到容斥系数f(x)=(1)xk(x1k1).

高维Min-Max容斥#

定义多元组的min算符为:min((x1,x2,,xn),(y1,y2,,yn))=(min(x1,y1),min(x2,y2),,min(xn,yn)).

可以证明,原来的minmax容斥在高维情况下也成立.

n=i=1kpiai,每个正整数的代表元素组为(a1,a2,,ak),那么该意义下的高维minmax容斥就等价于:

gcdi=1n{ai}=T{1,2,,n}lcmiT(1)|T|1{ai}

Epilogue#

第一篇总结现已施工完毕,如有错误请提出.

posted @   Parsnip  阅读(4266)  评论(5编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
目录