题目
题目描述
有$n$个点,它们从$1$到$n$进行标号,第$i$个点的限制为度数不能超过$A_i$
现在对于每个$s(1\leqslant s\leqslant n)$,问从这$n$个点中选出一些点组成大小为$s$的有标号无根树的方案数
$mod~1004535809$
输入格式
第一行一个整数$n$
第二行$n$个整数表示$A_i$
输出格式
输出一行$n$个整数,第$i$个整数表示$s=i$时的答案。
样例
样例输入
1 | 3 |
样例输出
1 | 3 3 2 |
数据范围与提示
$20\%$的数据:$n<6$
$60\%$的数据:$n\leqslant 50$
$100\%$的数据:$n\leqslant 100$
题解
要完成这一题,需要知道一个神奇的数列——prufer数列!
每一棵不同的无根树,都对应这不同的prufer数列
也就是说,prufer数列和无根树是一一对应的
所以我们只需要计算可以组成多少种prufer数列
prufer数列有一个神奇的性质——每一个节点出现的次数是它的度数减一
这就是我们为什么要使用prufer数列了
我们假设计算前$i$个点并且每个点的度数是$D_i$,那么总共就有$\frac{(i-2)!}{\sum \limits_{i=1}^{n}D_i!}$
这是一个简单的有限可重全排列
我们用$f_{i,j,k}$表示计算前$i$个点,已经选出了$j$个点,序列长度为$k$的方案数除以$(i-2)!$
那么,我们就可以进行转移:$f_{i+1,j,k}+=f_{i,j,k},f_{i+1,j+1,k+d}+=f_{i,j,k}\times \frac{1}{d!}$
那么,答案就是$f_{n,i,i-2}\times (i-2)!$
附上代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using namespace std;
int n,a[110];
long long MOD=1004535809,jc[110],ny[110],f[110][110][110];
long long POW(long long a,int b)
{
long long ans=1;
for(;b;b>>=1){
if(b&1) ans=ans*a%MOD;
a=a*a%MOD;
}
return ans;
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&n),jc[0]=ny[0]=f[0][0][0]=1;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),jc[i]=jc[i-1]*i%MOD;
ny[n]=POW(jc[n],MOD-2);
for(int i=n-1;i;i--) ny[i]=ny[i+1]*(i+1)%MOD;
for(int i=0;i<n;i++) for(int j=0;j<=i;j++) for(int k=0;k<=n;k++) if(f[i][j][k]){
f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%MOD;
int len=min(n-k-2,a[i+1]-1);
for(int l=0;l<=len;l++) f[i+1][j+1][k+l]=(f[i+1][j+1][k+l]+f[i][j][k]*ny[l]%MOD)%MOD;
}
printf("%d",n);
for(int i=2;i<=n;i++) printf(" %lld",f[n][i][i-2]*jc[i-2]%MOD);
}