当前位置: 首页 > news >正文

离散与组合数学 杂记

生成函数

概念

又称母函数把一个无穷数列 {an}\{a_n\}{an}(默认从 000 项起)表示成 G(x)=∑i≥0aixiG(x)=\displaystyle\sum_{i\ge0} a_ix^iG(x)=i0aixi 的函数形式。例如:

  • ai=2ia_i=2^iai=2iG(x)=∑i≥02ixiG(x)=\displaystyle\sum_{i\ge 0} 2^i x^iG(x)=i02ixi
  • ai=1a_i=1ai=1G(x)=∑i≥0xiG(x)=\displaystyle\sum _{i\ge 0} x^iG(x)=i0xi

应用

用来帮助推导数列的通项公式(比如斐波那契数列)也可以作为条件形式以数列形式给出的象征。一个特别的转化:逆运用等比数列求和公式,可以得到:11−ax=∑i≥0aixi\displaystyle\dfrac{1}{1-ax}=\sum_{i\ge0}a^ix^i1ax1=i0aixi,等号左边不带求和符号部分的,称为生成函数的封闭形式。

生成函数的乘法运算有意义,其卷积形式是函数直接相乘。对于 A(x)=∑i≥0aixiA(x)=\displaystyle\sum_{i\ge0}a_ix^iA(x)=i0aixiA(x)=∑i≥0bixiA(x)=\displaystyle\sum_{i\ge0}b_ix^iA(x)=i0bixi
A×B=∑i≥0n(xi∑j=0iajbi−j)A\times B=\sum_{i\ge0}^n\left(x^i\sum_{j=0}^ia_jb_{i-j}\right)A×B=i0n(xij=0iajbij)

洛谷 P10780 BZOJ3028 食物

题意

题目传送门。

思路

根据上文,将这个背包问题转化为,考虑每一种物品拿的个数的生成函数,并用封闭形式表示:

  • 偶数个形如 {1,0,1,0,1,......}\{1,0,1,0,1,......\}{1,0,1,0,1,......}∑k≥0x2k=11−x2\displaystyle\sum_{k\ge 0} x^{2k}=\frac{1}{1-x^2}k0x2k=1x21
  • 000111 个形如 {1,1,0,0,0,......}\{1,1,0,0,0,......\}{1,1,0,0,0,......}1+x1+x1+x
  • 000111222 个形如 {1,1,1,0,0,......}\{1,1,1,0,0,......\}{1,1,1,0,0,......}1+x+x21+x+x^21+x+x2
  • 奇数个形如 {0,1,0,1,0,......}\{0,1,0,1,0,......\}{0,1,0,1,0,......}∑k≥0x2k+1=x∑k≥0x2k=x1−x2\displaystyle\sum_{k\ge 0}x^{2k+1}=x\displaystyle\sum_{k\ge 0} x^{2k}=\frac{x}{1-x^2}k0x2k+1=xk0x2k=1x2x
  • 444 的倍数个类似第一条:∑k≥0x4k=11−x4\displaystyle\sum_{k\ge 0}x^{4k}=\frac{1}{1-x^4}k0x4k=1x41
  • 000111222333 个形如 {1,1,1,0,0,......}\{1,1,1,0,0,......\}{1,1,1,0,0,......}1+x+x2+x31+x+x^2+x^31+x+x2+x3
  • 333 的倍数个类似第一条:∑k≥0x3k=11−x3\displaystyle\sum_{k\ge 0}x^{3k}=\frac{1}{1-x^3}k0x3k=1x31.

全部乘起来,并用二项式定理化简 1+x+x21+x+x^21+x+x21+x+x2+x31+x+x^2+x^31+x+x2+x3
x(1−x)4\frac{x}{(1-x)^4}(1x)4x

考虑推 1(1−x)k=(11−x)k\dfrac{1}{(1-x)^k}=\left(\dfrac{1}{1-x}\right)^k(1x)k1=(1x1)k 是哪个生成函数的封闭形式与该生成函数的第 nnn 项系数,因为 11−ax=∑i≥0aixi\displaystyle\dfrac{1}{1-ax}=\sum_{i\ge0}a^ix^i1ax1=i0aixia=1a=1a=1 时,11−x=1+x+x2+x3+...\dfrac{1}{1-x}=1+x+x^2+x^3+...1x1=1+x+x2+x3+...kkk 次方展开后,相当于选 kkk 个自然数和为 nnn 的方案数:先给 kkk 个数加 111 转化问题为 kkk 个正整数和为 n+kn+kn+k,运用插板法得知方案数为 (n+k−1k−1)\dbinom{n+k-1}{k-1}(k1n+k1)

回到这一题 1(1−x)4\frac{1}{(1-x)^4}(1x)41 的第 n−1n-1n1 项系数为 (n−23)\dbinom{n-2}{3}(3n2),乘回 xxx 后即为答案。代码略。

Polya 计数

等我完全理解完再回来补。先给结论:用 kkk 中颜色给 nnn 个点的环染色方案数为 1n∑i=0n−1kgcd⁡(n,i)\dfrac{1}{n}\displaystyle\sum_{i=0}^{n-1}k^{\gcd(n,i)}n1i=0n1kgcd(n,i),可否感性理解呢?

洛谷 P4980 【模板】Pólya 定理

题意

nnn 种颜色给 nnn 个点染色的方案数。两种方案相同,当且仅当一种方案能够旋转变为“另一方案”。

n≤109n\le 10^9n109

思路

问题是如何快速算出 1n∑i=0n−1ngcd⁡(n,i)\dfrac{1}{n}\displaystyle\sum_{i=0}^{n-1}n^{\gcd(n,i)}n1i=0n1ngcd(n,i)。不妨暴力枚举 nnn 的约数 d=gcd⁡(n,i)d=\gcd(n,i)d=gcd(n,i),变为:
1n∑d∣nndφ(nd)\dfrac{1}{n}\displaystyle\sum_{d|n}n^d\varphi(\frac{n}{d})n1dnndφ(dn)

nnn 太大了不能预处理 φ\varphiφ,记得勤取模。时间复杂度为 T(n)=∑i≥0{O(i)+O(n/i)}=O(n3/4)T(n)=\displaystyle\sum_{i\ge 0}\{O(\sqrt{i})+O(\sqrt{n/i})\}=O(n^{3/4})T(n)=i0{O(i)+O(n/i)}=O(n3/4)(根据主定理推得)。代码略。

第二类斯特林数

为什么先说第二类,因为第二类好像常用一些。

斯特林数将两个典型的模型的方案数求解变成一个类似组合数的东西。

概念

{nm}\begin{Bmatrix} n\\ m \end{Bmatrix}{nm} 表示,将 nnn 个不同的球放进 mmm 个相同的盒子里,且每个盒子都非空的方案数。

其递推式的推导:

  • nnn 单开一个盒子,那么剩下 n−1n-1n1 个球放进 m−1m-1m1 个盒子:{n−1m−1}\begin{Bmatrix} n-1\\ m-1 \end{Bmatrix}{n1m1}
  • nnn 放进非空的 mmm 个盒子:m{n−1m}m\begin{Bmatrix} n-1\\ m \end{Bmatrix}m{n1m}

所以 {nm}={n−1m−1}+m{n−1m}\begin{Bmatrix} n\\ m \end{Bmatrix}=\begin{Bmatrix} n-1\\ m-1 \end{Bmatrix}+m\begin{Bmatrix} n-1\\ m \end{Bmatrix}{nm}={n1m1}+m{n1m}

特别的,{n0}=[n=0]\begin{Bmatrix} n\\ 0 \end{Bmatrix}=[n=0]{n0}=[n=0],中括号表示艾弗森括号。

可以 O(n2)O(n^2)O(n2) 预处理。

应用

先证明其通项公式,发现其满足下降幂性质:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CF932E Team Work

题意

给定 n,kn,kn,k,求 ∑i=1n(ni)ik\displaystyle\sum_{i=1}^n\dbinom{n}{i}i^ki=1n(in)ik

n≤109n\le 10^9n109k≤5000k\le 5000k5000

思路

把组合数用阶乘拆开,再运用斯特林数下降幂的性质:

∑i=0nn!i!(n−i)!∑j=0k{kj}i!(i−j)!\sum_{i=0}^n\frac{n!}{i!(n-i)!}\sum_{j=0}^k\begin{Bmatrix} k\\ j \end{Bmatrix}\frac{i!}{(i-j)!}i=0ni!(ni)!n!j=0k{kj}(ij)!i!

改变 iiijjj 的枚举顺序,iiijjj 开始到 nnn
∑j=0k{kj}∑i=jnn!(n−i)!(i−j)!\sum_{j=0}^k\begin{Bmatrix} k\\ j \end{Bmatrix}\sum_{i=j}^n\frac{n!}{(n-i)!(i-j)!}j=0k{kj}i=jn(ni)!(ij)!n!

强制变成组合数:
∑j=0k{kj}n!(n−j)!∑i=jn(n−j)!(n−i)!(i−j)!\sum_{j=0}^k\begin{Bmatrix} k\\ j \end{Bmatrix}\frac{n!}{(n-j)!}\sum_{i=j}^n\frac{(n-j)!}{(n-i)!(i-j)!}j=0k{kj}(nj)!n!i=jn(ni)!(ij)!(nj)!

∑j=0k{kj}n!(n−j)!∑i=jn(n−ji−j)\sum_{j=0}^k\begin{Bmatrix} k\\ j \end{Bmatrix}\frac{n!}{(n-j)!}\sum_{i=j}^n\binom{n-j}{i-j}j=0k{kj}(nj)!n!i=jn(ijnj)

化成 222 次幂形式:
∑j=0k{nm}n!(n−j)!∑i=0n−j(n−ji)\sum_{j=0}^k\begin{Bmatrix} n\\ m \end{Bmatrix}\frac{n!}{(n-j)!}\sum_{i=0}^{n-j}\binom{n-j}{i}j=0k{nm}(nj)!n!i=0nj(inj)

∑j=0k{kj}n!(n−j)!×2n−j\sum_{j=0}^k\begin{Bmatrix} k\\ j \end{Bmatrix}\frac{n!}{(n-j)!}\times2^{n-j}j=0k{kj}(nj)!n!×2nj

O(k2)O(k^2)O(k2) 时间复杂度预处理第二类斯特林数即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=5002,mod=1e9+7;
ll n,k;
ll S2[N][N],p2[N];
ll qpow(ll x,ll k)
{ll ret=1;while(k){if(k&1)ret=ret*x%mod;x=x*x%mod;k>>=1;}return ret;
}
void init()
{S2[0][0]=1;for(int i=1;i<N;i++)for(int j=1;j<N;j++)S2[i][j]=(S2[i-1][j-1]+j*S2[i-1][j]%mod)%mod;
}
int main()
{scanf("%lld%lld",&n,&k);init();ll ans=0;for(int j=0;j<=min(n,k);j++){ll ret=S2[k][j]*qpow(2,n-j)%mod,mul=1;for(int t=n-j+1;t<=n;t++)ret=ret*t%mod;ans=(ans+ret)%mod;}printf("%lld",ans);return 0;
}

第一类斯特林数

概念

[nm]\begin{bmatrix} n\\ m \end{bmatrix}[nm] 表示,将 nnn 个不同的球摆成 mmm 个环的方案数。两个环相同,当且仅当一个环通过旋转可以变成第二个环。

其递推式的推导:

  • nnn 单开一个环,那么剩下 n−1n-1n1 个球成 m−1m-1m1 个环:[n−1m−1]\begin{bmatrix} n-1\\ m-1 \end{bmatrix}[n1m1]
  • nnn 放进 mmm 个环,在放了的 (n−1)(n-1)(n1) 个球后面都可以放:(n−1)[n−1m](n-1)\begin{bmatrix} n-1\\ m \end{bmatrix}(n1)[n1m]

所以 [nm]=[n−1m−1]+(n−1)[n−1m]\begin{bmatrix} n\\ m \end{bmatrix}=\begin{bmatrix} n-1\\ m-1 \end{bmatrix}+(n-1)\begin{bmatrix} n-1\\ m \end{bmatrix}[nm]=[n1m1]+(n1)[n1m]

特别的,[nm]=[n=0]\begin{bmatrix} n\\ m \end{bmatrix}=[n=0][nm]=[n=0],中括号为艾弗森括号。

洛谷 P4609 FJOI2016 建筑师

题意

小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 nnn 个建筑,每个建筑的高度是 111nnn 之间的一个整数。

小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 AAA 个建筑,从最右边(所有建筑都在左边)看能看到 BBB 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?

如果建筑 iii 的左(右)边没有任何建造比它高,则建筑 iii 可以从左(右)边看到。两种方案不同,当且仅当存在某个建筑在两种方案下的高度不同。

1≤n≤50000,1≤A,B≤100,1≤T≤2000001 \leq n \leq 50000, \ 1 \leq A, B \leq 100, \ 1 \leq T \leq 2000001n50000, 1A,B100, 1T200000

思路

中间那个左右都能看见的,高度必然是极值 nnn

除去中间的,左边要看到 a−1a-1a1 个,右边要看到 b−1b-1b1 个。不妨将能看到的和其左/右边比它矮的看成 a−1+b−1=a+b−2a-1+b-1=a+b-2a1+b1=a+b2 个环(反正互换位置之后,尽管某些环的元素种类改变,但是环的个数不改变),就可以用第一类斯特林数的含义,将剩下 n−1n-1n1 和分成 a+b−2a+b-2a+b2 个环,即 [n−1a+b−2]\begin{bmatrix} n-1\\ a+b-2 \end{bmatrix}[n1a+b2]

然后再 a+b−2a+b-2a+b2 个环中选各自最大值 a−1a-1a1 个,成为左边能看见的(排列方式唯一,必然是从矮到高),即 (a+b−2a−1)\dbinom{a+b-2}{a-1}(a1a+b2)

于是答案为 [n−1a+b−2](a+b−2a−1)\begin{bmatrix} n-1\\ a+b-2 \end{bmatrix}\dbinom{a+b-2}{a-1}[n1a+b2](a1a+b2)

预处理第一类斯特林数即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=5e4+9,M=202,mod=1e9+7;
ll T;
ll S1[N][M],fac[N],inv[N];
ll qpow(ll x,ll k)
{ll ret=1;while(k){if(k&1)ret=ret*x%mod;x=x*x%mod;k>>=1;}return ret;
}
void init()
{S1[0][0]=1;for(int i=1;i<N;i++)for(int j=1;j<M;j++)S1[i][j]=(S1[i-1][j-1]+(i-1)*S1[i-1][j]%mod)%mod;fac[0]=1;for(int i=1;i<N;i++)fac[i]=fac[i-1]*i%mod;inv[N-1]=qpow(fac[N-1],mod-2);for(int i=N-2;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll C(ll n,ll m)
{return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{scanf("%lld",&T);init();while(T--){ll n,a,b;scanf("%lld%lld%lld",&n,&a,&b);printf("%lld\n",S1[n-1][a+b-2]*C(a+b-2,a-1)%mod);}return 0;
}

无根树的 prufer 序列

概念

prufer 序列将节点数为 nnn 的无根树转化为长为 n−2n-2n2 的序列。其表示期望与根的编号无关——定义为每次选择一个编号最小的叶节点并删除,然后在序列中记录其所连接的节点编号,直到剩下两个节点。

在这里插入图片描述
如上图是一棵有 777 个节点的树的 prufer 序列形成过程。

用 prufer 序列同样可以还原出一棵树来。

求法

求一棵树的 prufer 序列。可以维护每个节点的度数,以节点编号为第一关键字扔进堆里排序,当度为 000 时舍弃这个节点。容易做到 O(nlog⁡n)O(n\log n)O(nlogn)

O(n)O(n)O(n) 的更优秀的算法:“剥叶子”的过程中,每次只会让其父亲节点度数减 111,因此每删除一个叶节点,至多产生一个新叶节点。

因此可以用一个单调指针 pospospos 维护当前度数为 000 的最小结点,pospospos 枚举到了先删 pospospos,然后看 faposfa_{pos}fapos 是否度数变为 000,如果为 000 就顺带删去了;没准是条链,因此可以 while 一路删上去。

用 prufer 还原一棵树维护序列中节点的待加边,当儿子连完、待加边归 000,就连向序列中下一个节点——似乎完全就是求 prufer 的相反操作,具体看下面的代码。

P6086 【模板】Prufer 序列

思路

如上所述。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e5+9;
ll n,m,mod,k;
ll fa[N],siz[N];
ll fz(ll x)
{while(x!=fa[x])x=fa[x]=fa[fa[x]];return x;
}
void join(ll u,ll v)
{ll fu=fz(u),fv=fz(v);if(fu==fv)return;fa[fu]=fv;siz[fv]+=siz[fu];k--;
}
ll qpow(ll x,ll k)
{ll ret=1;while(k){if(k&1)ret=ret*x%mod;x=x*x%mod;k>>=1;}return ret;
}
int main()
{scanf("%lld%lld%lld",&n,&m,&mod);if(mod==1){puts("0");return 0;}k=n;for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;for(int i=1;i<=m;i++){ll u,v;scanf("%lld%lld",&u,&v);join(u,v);}if(k==1){puts("1");return 0;}ll ans=1;for(int i=1;i<=n;i++)if(i==fz(i))ans=ans*siz[i]%mod;ans=ans*qpow(n,k-2)%mod;printf("%lld",ans);return 0;
}

prufer 序列怎么求其实应用不广,关键是其本身所带有的性质更为重要。

应用

  • 把一个树化为 prufer 序列,是建立 nnn 个节点无根树和 n−2n-2n2 个元素的序列的双射。

  • 一个节点在 prufer 序列中出现次数为其度数减 111

  • Cayley 公式:对 nnn 个节点建立一棵无根树,有 nn−2n^{n-2}nn2 种方案。

    证明:任意一个长度为 n−2n-2n2 的值域 [1,n][1,n][1,n] 的整数序列都可以通过 prufer 序列双射对应一个生成树,于是方案数就是 nn−2n^{n-2}nn2

CF156D Clues

题意

给定一个 nnn 个点 mmm 条边的带标号无向图,若其有 kkk 个连通块,想要添加 k−1k-1k1 条边使得整个图连通。求方案数。

1≤n≤1051\le n\le 10^51n1050≤m≤1050\le m\le 10^50m1051≤k≤1091\le k\le 10^91k109

思路

这是一道经典问题。

设每个连通块有 sis_isi 个节点,did_idi 为连通块的度数—— prufer 序列就是用度数所推。

在这里插入图片描述
在这里插入图片描述
计算上式即可。代码略。

洛谷 P2290 HNOI2004 树的计数

题意

一个有 nnn 个节点的树,设它的节点分别为 v1,v2,…,vnv_1,v_2,\ldots,v_nv1,v2,,vn,已知第 iii 个节点 viv_ivi 的度数为 did_idi,问满足这样的条件的不同的树有多少棵。

1≤n≤1501\le n\le 1501n150,保证满足条件的树不超过 101710^{17}1017 个。

思路

其实方案数就是上一篇题解所提,(n−2d1−1,d2−1,d3−1,......,dn−1)=(n−2)!∏i=1n(di−1)!\dbinom{n-2}{d_1-1,d_2-1,d_3-1,......,d_n-1}=\dfrac{(n-2)!}{\displaystyle\prod_{i=1}^n(d_i-1)!}(d11,d21,d31,......,dn1n2)=i=1n(di1)!(n2)!

好像会爆 long long,阶乘的增长速度超乎想象,倒不如用它的普通组合数意义形式。设 si=di−1s_i=d_i-1si=di1sum=n−2sum=n-2sum=n2(理论上是 ∑si\sum s_isi,如果不是就不合法)。答案就是 ∏i=1n(sum−∑j=1i−1sjsi)\prod_{i=1}^n\binom{sum-\displaystyle\sum_{j=1}^{i-1}s_j}{s_i}i=1n(sisumj=1i1sj)

代码略。

洛谷 P2624 HNOI2008 明明的烦恼

题意

给出标号为 111nnn 的点,以及某些点最终的度数,允许在任意两点间连线,可产生多少棵度数满足要求的树?无解就输出 −1-11

1≤n≤10001\le n\le10001n1000

思路

这是上一题的加强版。

对于已知的 mmm 个点,我们可以直接套用上面的 (m−2d1−1,d2−1,d3−1,......,dm−1)=(m−2)!∏i=1m(di−1)!\dbinom{m-2}{d_1-1,d_2-1,d_3-1,......,d_m-1}=\dfrac{(m-2)!}{\displaystyle\prod_{i=1}^m(d_i-1)!}(d11,d21,d31,......,dm1m2)=i=1m(di1)!(m2)! 公式,prufer 序列上已经选了的方案数为 (n−2∑i=1m(di−1))\dbinom{n-2}{\displaystyle\sum_{i=1}^m(d_i-1)}(i=1m(di1)n2),最后再把剩下的 n−mn-mnm 个点,塞进 n−2−∑i=1m(di−1)n-2-\displaystyle\sum_{i=1}^m(d_i-1)n2i=1m(di1) 去:
(n−2∑i=1m(di−1))(m−2)!∏i=1m(di−1)!×(n−m)n−2−∑i=1m(di−1)\dbinom{n-2}{\displaystyle\sum_{i=1}^m(d_i-1)}\dfrac{(m-2)!}{\displaystyle\prod_{i=1}^m(d_i-1)!}\times(n-m)^{n-2-\displaystyle\sum_{i=1}^m(d_i-1)}(i=1m(di1)n2)i=1m(di1)!(m2)!×(nm)n2i=1m(di1)

没得化回普通组合数,要用高精捏。代码略,有缘再写。

http://www.xdnf.cn/news/1138753.html

相关文章:

  • 学习设计模式《十八》——备忘录模式
  • AI安全威胁之MCP Server投毒攻击实践
  • 深入理解进程等待:wait的简化与waitpid的灵活性
  • centos中新增硬盘挂载文件夹
  • 【FFmpeg 快速入门】本地播放器 项目
  • 林曦词典|文质彬彬
  • 物联网主机在化工园区安全风险智能化管控平台中的应用
  • mongodb 入门级别操作
  • 搞清MVCC
  • 优化 CSS 性能
  • 面试Redis篇-深入理解Redis缓存击穿
  • Selenium 启动的浏览器自动退出问题分析
  • 全面升级!WizTelemetry 可观测平台 2.0 深度解析:打造云原生时代的智能可观测平台
  • 杭州卓健信息科技有限公司 Java 面经
  • web前端渡一大师课 CSS属性计算过程
  • 损失函数的等高线与参数置零的关系
  • 从AWS MySQL数据库下载备份到S3的完整解决方案
  • Linux操作系统之线程:线程概念
  • mongodb-org-mongos : Depends: libssl1.1 (>= 1.1.1) but it is not installable
  • Java使用FastExcel实现Excel文件导入
  • 镁合金汽车零部件市场报告:行业现状、发展趋势与投资前景分析
  • 集群聊天服务器各个类进行详解
  • Docker国内镜像
  • 关于用git上传远程库的一些常见命令使用和常见问题:
  • RuoYi-Cloud 定制微服务
  • Java集合框架中List常见问题
  • 【软件开发】主流 AI 编码插件
  • 服务器数据恢复—raid5磁盘阵列崩溃如何恢复数据?
  • 《每日AI-人工智能-编程日报》--2025年7月17日
  • Odoo最佳业务实践:从库存管理重构到全链路协同