Prufer序列与树的计数

Prufer序列

  • Prufer数列是有标号无根树的一种数列,点数为n的树转化来的Prufer数列长度为n-2。
  • 对于一棵确定的有标号无根树,对应着唯一确定的prufer序列。

构造方法

有标号无根树转化为Prufer序列

  1. 找到编号最小的度数为1的点。
  2. 删除该节点并在序列中添加与该节点相连的节点的编号。
  3. 重复1,21,2操作,直到整棵树只剩下两个节点

如下图的prufer序列为3,5,1,3

Prufer序列转化为有标号无根树

  1. 每次取出prufer序列中最前面的元素u
  2. 在点集中找到编号最小的没有在prufer序列中出现的元素v(没有出现就等于度数为一)
  3. 给u,v连边然后分别在两个集合中删除删除(连边的过程可以想象成删边的过程)
  4. 最后在点集中剩下两个节点,给它们连边

性质

  1. 一棵n个节点的有标号无根树唯一地对应了一个长度为n-2的数列,数列中的每个数都在1到n的范围内
  2. prufer序列中某个编号出现的次数就等于这个编号的节点在无根树中的度数-1
  3. n个点的有标号无根树有n^(n-2)个
  4. n个点的有标号有根树的个数为n^(n-2)*n=n^(n-1)
  5. n个度数分别为{d1,d2,d3,...,dn}的点组成的有标号无根树的个数为\frac{(n-2)!}{(b_{1}-1)!\cdot (b_{2}-1)\cdot ... \cdot(b_{n}-1)}

题目

  • 对于每个s就是求有多少个本质不同的长度为s-2的数列,且每个数出现次数小于A[i]
  • f[i][j][k]表示前i个数中选了j个数,总共有k个点的方案数
  • 如果不选当前点f[i][j][k]+=f[i-1][j][k]
  • 如果选当前点则枚举该点出现了l次 f[i][j][k]+=f[i-1][j-1][k-l]*C[k][l]
  • 时间复杂度O(n^4)

Code


#include<bits/stdc++.h>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define ll long long
using namespace std;
const int N=110;
const int mod=1004535809;
ll f[N][N][N],c[N][N],lim[N],n;
int main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%lld",&n);
	rep(i,1,n)scanf("%lld",&lim[i]);
	f[0][0][0]=1;
	rep(i,1,n){
		c[i][0]=c[0][0]=1;
		rep(j,1,n)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
	}
	rep(i,1,n){
		rep(j,0,i){
			rep(k,0,n-2){
				f[i][j][k]=f[i-1][j][k];
				if(!j)continue;
				rep(l,0,min(k,lim[i]-1)){
					f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-l]*c[k][l])%mod;
				}
			}
		}
	}
	printf("%lld ",n);rep(i,2,n)printf("%lld ",f[n][i][i-2]);
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值