从 P r u f e r Prufer Prufer编码到各种树的计数
1. P r u f e r Prufer Prufer编码
-
将无根树进行编码的一种方式,无根树就是边没有方向的树,即以树上的每一个点做根,只算一种方案。
-
无根树转序列做法
每次找到编号最小的叶子,输出它父亲的编号,然后删除,直到只剩2个节点。
- 序列转无根树做法
设集合 S = { 1 , 2 , ⋯ , n } S=\left \{ 1,2,\cdots,n \right \} S={1,2,⋯,n}
第 i i i次,找到在 S S S中没有在 p r u f e r prufer prufer编码第 i i i到 n − 2 n-2 n−2位出现的最小的数,将它与 p r u f e r prufer prufer编码的第 i i i位连边,并把它从S中删除。
最后剩下的两个数,它们两个连一条边即可。
代码实现的时候,可以将每个点的度数初始化为 p r u f e r prufer prufer序列中出现的次数+1,每次找度数为1最小的那一个与 p r u f e r prufer prufer序列当前位连边。
2. 有标号无根树的计数( C a y l e y Cayley Cayley公式)
-
由上面的做法显然可以得出,每一个长度为n-2的序列,唯一的对应了一棵无根树,且一颗无根树唯一的对应了一个序列。
-
因此可以得到n个点的有标号无根树总共有 n n − 2 n^{n-2} nn−2个。
-
性质与应用
-
在 p r u f e r prufer prufer编码中,每个点出现的次数为 该 点 度 数 − 1 该点度数-1 该点度数−1。
-
如果限制n个点的读数为 d 1 , d 2 , d 3 , ⋯ , d n d_1,d_2,d_3,\cdots,d_n d1,d2,d3,⋯,dn,那么这样的无根树共有
( n − 2 ) ! ( d 1 − 1 ) ! ( d 2 − 1 ) ! ( d 3 − 1 ) ! ⋯ ( d n − 1 ) ! 个 \frac {(n-2)!}{(d_1-1)!(d_2-1)!(d_3-1)!\cdots(d_n-1)!}个 (d1−1)!(d2−1)!(d3−1)!⋯(dn−1)!(n−2)!个
-
-
如果只限制了k个呢, p r u f e r prufer prufer编码剩了m个位置呢?那么假装限制了,再乘以随便填的方案数,即
( n − 2 ) ! ( d 1 − 1 ) ! ( d 2 − 1 ) ! ⋯ ( d k − 1 ) ! ( m ) ! ∗ ( n − k ) m \frac {(n-2)!}{(d_1-1)!(d_2-1)!\cdots(d_k-1)!(m)!}*(n-k)^m (d1−1)!(d2−1)!⋯(dk−1)!(m)!(n−2)!∗(n−k)m
3. 有标号有根树的计数
-
p
r
u
f
e
r
prufer
prufer编码是无根树,但是树形均不同,那么每个生成的树以每个点做根均可,因此共有
n n − 2 ∗ n = n n − 1 个 n^{n-2}*n=n^{n-1}个 nn−2∗n=nn−1个
4.无标号有根树的计数
-
方法1朴素形式,设 a n a_n an为树大小为n时的答案。设对于根,大小为k的子树共有 c k c_k ck个,而每个子树都可以选择 1 ∼ a k 1\sim a_k 1∼ak的一种方案,即 ∑ k = 1 a k x k = c k \sum_{k=1}^{a_k}x_k=c_k ∑k=1akxk=ck,该方程非负整数解个数为 ∏ k > 0 C a k + c k − 1 c k \prod_{k>0}C_{a_k+c_k-1}^{c_k} ∏k>0Cak+ck−1ck( a 1 = 1 a_1=1 a1=1)。
a n = ∑ c 1 + 2 c 2 + ⋯ + n c n = n − 1 ∏ k > 0 C a k + c k − 1 c k a_n=\sum_{c_1+2c_2+\cdots+nc_n=n-1}\prod_{k>0}C_{a_k+c_k-1}^{c_k} an=c1+2c2+⋯+ncn=n−1∑k>0∏Cak+ck−1ck -
方法1高级形式经过复杂的母函数推导,我们得到
令 s n , j = ∑ 1 ≤ i ≤ n j a n + 1 − i ∗ j , 则 有 s n , j = s n − j , j + a n + 1 − j 则 有 a n + 1 = ∑ 1 ≤ j ≤ n j a j s n , j n 令s_{n,j}=\sum_{1\leq i \leq \frac n j}a_{n+1-i*j},则有s_{n,j}=s_{n-j,j}+a_{n+1-j}\\ 则有a_{n+1}=\frac {\sum_{1\leq j \leq n}ja_js_{n,j}} {n} 令sn,j=1≤i≤jn∑an+1−i∗j,则有sn,j=sn−j,j+an+1−j则有an+1=n∑1≤j≤njajsn,j -
方法2朴素形式如果我们把定义改为 f n , j f_{n,j} fn,j表示树大小为n,根节点度数为j的方案数,那么 a n = ∑ j = 1 n f n , j a_n=\sum_{j=1}^nf_{n,j} an=∑j=1nfn,j,就有
f i , j = ∑ c 1 + 2 c 2 + ⋯ + j c j = i − 1 ∏ k > 0 j C a k + c k − 1 c k f_{i,j}=\sum_{c_1+2c_2+\cdots+jc_j=i-1}\prod_{k>0}^jC_{a_k+c_k-1}^{c_k} fi,j=c1+2c2+⋯+jcj=i−1∑k>0∏jCak+ck−1ck -
方法2高级形式我们考虑对 f i , j f_{i,j} fi,j进行 d p dp dp,可以通过限制最大子树进行背包,即从小到大枚举 m a x s o n maxson maxson,则有
f i , j = ∑ k = 1 m a x { j , ⌊ i m a x s o n ⌋ } C a m a x s o n + k − 1 k f i − k ∗ m a x s o n , j − k f_{i,j}=\sum_{k=1}^{max\left\{j,\lfloor\frac i {maxson\rfloor}\right\}}C_{a_{maxson}+k-1}^k f_{i-k*maxson,j-k} fi,j=k=1∑max{j,⌊maxson⌋i}Camaxson+k−1kfi−k∗maxson,j−k -
这样有一个好处就是可以限制最大度数,如构造烷烃。
5.无标号无根树的计数
-
利用一下上题的 a n a_n an
-
方法1,设 b n b_n bn为本题的答案,根据质心的关系,可以得到
n 为 奇 数 时 b n = a n − ∑ 0 < i ≤ ⌊ n 2 ⌋ a i a n − i n 为 偶 数 时 b n = a n − ∑ 0 < i ≤ n a i a n − i + 1 2 a n 2 ( a n 2 + 1 ) n为奇数时\\b_n=a_n-\sum_{0<i\leq \lfloor\frac n 2\rfloor}a_ia_{n-i}\\n为偶数时\\b_n=a_n-\sum_{0<i\leq n}a_ia_{n-i}+\frac 1 2 a_{\frac n 2}(a_{\frac n 2}+1) n为奇数时bn=an−0<i≤⌊2n⌋∑aian−in为偶数时bn=an−0<i≤n∑aian−i+21a2n(a2n+1) -
方法2,可以通过枚举重心的方法来解决,强制 m a x s o n < ⌈ n 2 ⌉ maxson<\lceil\frac n 2 \rceil maxson<⌈2n⌉,则保证是是以重心为根的无标号有根树计数,但如果n为偶数,可能会有两个重心,这两个重心子树大小都是 n 2 \frac n 2 2n,因此最终结果为
a n s = ∑ j = 0 m f n , j + [ n m o d 2 = 0 ] C a n 2 + 1 2 ans=\sum_{j=0}^mf_{n,j}+[n\ mod\ 2\ =0] C_{a_{\frac n 2 +1}}^2 ans=j=0∑mfn,j+[n mod 2 =0]Ca2n+12 -
烷烃例题 [BZOJ4271]chemistry,方法2(没有加高精度)代码如下
//无高精度版本 #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> #define maxn 505 using namespace std; typedef long long ll; int n,m; ll dp[maxn][maxn]; ll c(ll n,ll m) { ll ans=1; for(int i=m;i>=1;i--) { ans=ans*n; n--; } for(int i=m;i>1;i--) { ans/=i; } return ans; } int main() { dp[1][0]=1; scanf("%d",&n); m=4; for(int maxson=1;maxson<((n+1)/2);maxson++) { ll a=0; for(int j=0;j<m;j++) a+=dp[maxson][j]; for(int i=n;i>maxson;i--) { for(int j=1;j<=m;j++) { for(int k=1;k<=j&&maxson*k<i;k++) { dp[i][j]+=dp[i-maxson*k][j-k]*c(a+k-1,k); } } } } ll ans=0; for(int j=0;j<=m;j++) ans+=dp[n][j]; if(!(n&1)) { ll a=0; for(int j=0;j<m;j++) a+=dp[n/2][j]; ans+=c(a+1,2); } printf("%lld\n",ans); }
参考资料
- 顾誉州WC交流https://wenku.baidu.com/view/3fbabbcc647d27284a735113.html
- 华师一附中赵爽论文 https://max.book118.com/html/2018/0403/159821895.shtm
- Flashhu https://www.cnblogs.com/flashhu/p/9457830.html