purfer序列

p u r f e r \bm{purfer} purfer序列

  • 概念:

转换 1 1 1:无根树 → \to p u r f e r purfer purfer序列

现在,给一棵树,我们考虑如何把它变成 p r e f u r prefur prefur序列。

我们需要重复进行以下操作,直至树中只剩下两个点:

  • 找到一个度数为 1 1 1,且编号最小的点(其中编号最小保证了后面将会提到的 p r u f e r prufer prufer序列的唯一对应性,同时也方便从 p r u f e r prufer prufer序列转化回无根树)
  • 把这个点的父亲节点加入序列,然后把这个点从树中删除。

以上面的图为例,我们可以模拟这一过程如下:

  • 找到 4 4 4号节点,将其父结点加入序列,然后将其删去。此时序列: { 2 } \{ 2 \} {2}
  • 找到 5 5 5号节点,将其父结点加入序列,然后将其删去。此时序列: { 2 , 3 } \{ 2,3 \} {2,3}
  • 找到 3 3 3号节点,将其父结点加入序列,然后将其删去。此时序列: { 2 , 3 , 1 } \{ 2,3,1 \} {2,3,1}
  • 找到 6 6 6号节点,将其父结点加入序列,然后将其删去。此时序列: { 2 , 3 , 1 , 2 } \{ 2,3,1,2 \} {2,3,1,2}
  • 找到 2 2 2号节点,将其父结点加入序列,然后将其删去。此时序列: { 2 , 3 , 1 , 2 , 1 } \{ 2,3,1,2,1 \} {2,3,1,2,1}

所以,最后得到的 p r u f e r prufer prufer序列就是 { 2 , 3 , 1 , 2 , 1 } \{2,3,1,2,1\} {2,3,1,2,1}

转换 2 2 2 p u r f e r purfer purfer序列 → \to 无根树

还是以刚才那棵树为例,我们要考虑如何把它的 p r e f u r prefur prefur序列变回它本身。

我们需要重复进行以下操作,直至点集中只剩下两个点:(初始化所有点都在点集中)

  • 取出 p r u f e r prufer prufer序列最前面的元素 x x x
  • 取出在点集中的、且当前不在 p r u f e r prufer prufer序列中的最小元素 y y y
  • x , y x,y x,y之间连接一条边

最后,我们在点集中剩下的两个点中连一条边。

显然这有 n − 1 n−1 n1条边,且绝对不会形成环,因此它是一棵树,且就是原树。

以上面的序列为例,我们可以模拟这一过程如下:

  • 取出 2 , 4 2,4 2,4连边。此时 p r u f e r prufer prufer序列: { 3 , 1 , 2 , 1 } \{3,1,2,1\} {3,1,2,1},点集: { 1 , 2 , 3 , 5 , 6 , 7 } \{1,2,3,5,6,7\} {1,2,3,5,6,7}
  • 取出 3 , 5 3,5 3,5连边。此时 p r u f e r prufer prufer序列: { 1 , 2 , 1 } \{1,2,1\} {1,2,1},点集: { 1 , 2 , 3 , 6 , 7 } \{1,2,3,6,7\} {1,2,3,6,7}
  • 取出 1 , 3 1,3 1,3连边。此时 p r u f e r prufer prufer序列: { 2 , 1 } \{2,1\} {2,1},点集: { 1 , 2 , 6 , 7 } \{1,2,6,7\} {1,2,6,7}
  • 取出 2 , 6 2,6 2,6连边。此时 p r u f e r prufer prufer序列: { 1 } \{1\} {1},点集: { 1 , 2 , 7 } \{1,2,7\} {1,2,7}
  • 取出 1 , 2 1,2 1,2连边。此时 p r u f e r prufer prufer序列: { } \{\} {},点集: { 1 , 7 } \{1,7\} {1,7}
  • 最后再在 1 , 7 1,7 1,7间连边,就可以得到原树

p r u f e r prufer prufer序列的性质及相关结论:

  • p r u f e r prufer prufer序列与无根树一一对应
  • 度数为 d i d_i di的节点会在 p r u f e r prufer prufer序列中出现 d i − 1 d_{i-1} di1
  • 一个 n n n个节点的完全图的生成树个数为 n n − 2 n^{n-2} nn2
    对于一个 n n n个点的无根树,它的 p r u f e r prufer prufer序列长为 n − 2 n−2 n2,而每个位置有 n n n种可能性,因此可能的 p r u f e r prufer prufer序列有 n n − 2 n^{n-2} nn2
    又由于 p r u f e r prufer prufer序列与无根树一一对应,因此生成树个数应与 p r u f e r prufer prufer序列种树相同,即 n n − 2 n^{n-2} nn2
  • 对于给定度数为 d 1 ∼ n d_{1\sim n} d1n的一棵无根树共有 ( n − 2 ) ! ∏ i = 1 n ( d i − 1 ) ! \frac{(n-2)!}{\prod_{i=1}^n(d_i-1)!} i=1n(di1)!(n2)!种情况。
    由上面的性质可以知道,度数为 d i d_i di的节点会在 p r u f e r prufer prufer序列中出现 d i − 1 d_i-1 di1次。
    则就是要求出 d i − 1 d_i-1 di1 i ( 1 ≤ i ≤ n ) i(1\le i\le n) i(1in)的全排列个数。
    而上面那个式子就是可重全排列公式(即全排列个数除以重复元素内部的全排列个数)

p u r f e r purfer purfer序列到底有啥实际意义也不是很清楚

  • 例题

P2290 [HNOI2004]树的计数

思路:利用性质:对于给定度数为 d 1 ∼ n d_{1\sim n} d1n的一棵无根树共有 ( n − 2 ) ! ∏ i = 1 n ( d i − 1 ) ! \frac{(n-2)!}{\prod_{i=1}^n(d_i-1)!} i=1n(di1)!(n2)!种情况。

直接算阶乘的话肯定会炸··· 这里我们先记 d i = d i − 1 d_i=d_i-1 di=di1,再将柿子展开变形:
( n − 2 ) ! ∏ i = 1 n d i ! = ( n − 2 ) ! d 1 ! ∗ ( n − 2 − d 1 ) ! ∗ ( n − 2 − d 1 ) ! d 2 ! ∗ ( n − 2 − d 1 − d 2 ) ! ∗ ⋅ ⋅ ⋅ ∗ ( n − 2 − ∑ d i ) ! d n ! ∗ ( n − 2 − ∑ d i ) ! ( n − 2 ) ! ∏ i = 1 n d i ! = ( n − 2 d 1 ) ∗ ( n − 2 − d 1 d 2 ) ∗ ⋅ ⋅ ⋅ ∗ ( n − 2 − ( ∑ d i − d n ) d n − 1 ) ∗ ( n − 2 − ∑ d i d n ) \frac{(n-2)!}{\prod_{i=1}^n d_i!}=\frac{(n-2)!}{d_1!*(n-2-d_1)!}*\frac{(n-2-d_1)!}{d_2!*(n-2-d_1-d_2)!}*···*\frac{(n-2-\sum d_i)!}{d_n!*(n-2-\sum d_i)!}\\ \quad\\ \frac{(n-2)!}{\prod_{i=1}^n d_i!}={n-2 \choose d_1}*{n-2-d_1 \choose d_2}*···*{n-2-(\sum d_i-d_n) \choose d_{n-1}}*{n-2-\sum d_i \choose d_n} i=1ndi!(n2)!=d1!(n2d1)!(n2)!d2!(n2d1d2)!(n2d1)!dn!(n2di)!(n2di)!i=1ndi!(n2)!=(d1n2)(d2n2d1)(dn1n2(didn))(dnn2di)
因此求这 n n n个组合数即可

PS:注意特判:只有一个节点时,度数不为 0 0 0则不成立; ∑ ( d i − 1 )   ! = n − 2 \sum (d_i-1)\ !=n-2 (di1) !=n2则不成立

Code:

#include <bits/stdc++.h>
using namespace std;
const int N=200+10;
typedef long long ll;
int n,d[N],c[N][N];
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>d[i];
	int sum=0;
	if(n==1){
		if(d[1])cout<<"0"<<endl;
		else cout<<"1"<<endl;
		return 0;
	}
	for(int i=0;i<=n;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++){
			c[i][j]=c[i-1][j]+c[i-1][j-1];
		}
	}
	for(int i=1;i<=n;i++){
		if(!d[i]) {
			cout<<"0"<<endl;
			return 0;
		}
		d[i]--;
		sum+=d[i];
	}
	if(sum!=n-2) {
		cout<<"0"<<endl;
		return 0;
	}
	sum=0;
	ll ans=1;
	for(int i=1;i<=n;i++){
		ans*=c[n-2-sum][d[i]];
		sum+=d[i];
	}
	cout<<ans<<endl;
	return 0;
}

P4981 父子

思路:利用性质:一个 n n n个节点的完全图的生成树个数为 n n − 2 n^{n-2} nn2
本题题意是求有根树,故答案为: n ∗ n n − 2 n*n^{n-2} nnn2 n n − 1 n^{n-1} nn1

Code:

#include <bits/stdc++.h>
using namespace std;
const int N=200+10,mod=1e9+9;
typedef long long ll;

ll n,t;
ll power(ll a,ll b){
	ll ans=1;
	for(;b;b>>=1){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
	}
	return ans;
} 
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		ll ans=power(n,n-1);
		cout<<ans<<endl;
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不会数据库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值