Prufer序列
- Prufer数列是有标号无根树的一种数列,点数为n的树转化来的Prufer数列长度为n-2。
- 对于一棵确定的有标号无根树,对应着唯一确定的prufer序列。
构造方法
有标号无根树转化为Prufer序列
- 找到编号最小的度数为1的点。
- 删除该节点并在序列中添加与该节点相连的节点的编号。
- 重复1,21,2操作,直到整棵树只剩下两个节点
如下图的prufer序列为3,5,1,3
Prufer序列转化为有标号无根树
- 每次取出prufer序列中最前面的元素u
- 在点集中找到编号最小的没有在prufer序列中出现的元素v(没有出现就等于度数为一)
- 给u,v连边然后分别在两个集合中删除删除(连边的过程可以想象成删边的过程)
- 最后在点集中剩下两个节点,给它们连边
性质
- 一棵n个节点的有标号无根树唯一地对应了一个长度为n-2的数列,数列中的每个数都在1到n的范围内
- prufer序列中某个编号出现的次数就等于这个编号的节点在无根树中的度数-1
- n个点的有标号无根树有n^(n-2)个
- n个点的有标号有根树的个数为n^(n-2)*n=n^(n-1)
- n个度数分别为{d1,d2,d3,...,dn}的点组成的有标号无根树的个数为
题目
- 对于每个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;
}