题目大意
有
n
个点,从
对于每个
s(1≤s≤n)
,问从
n
个点中选出一些点组成大小为
1≤n≤100
题目分析
既然是无根树计数,那就考虑使用prufer序列。
一个节点数为
s
的树对应于一个长度为
一个长度为
s
的prufer序列,假设出现了
s!∏mi=1ci!
怎么限制度数?一个点的度数就是它在prufer序列上出现的次数加一。
考虑使用dp来计数,假设我们考虑到第 i 个点,已经选择了
那么一个 fi,j,k 能够转移到的位置有:
fi+1,j,k+=fi,j,k
或者
fi+1,j+1,k+c+=fi,j,k×1c!(其中c<ai+1)
最后统计答案,一个大小是 s 的带标号无根树的种类数就是
时间复杂度 O(n4) 。当然,可以发现转移过程是一个卷积,使用FFT加速的话,可以做到 O(n3logn) .
代码实现
#include <iostream>
#include <cstdio>
using namespace std;
const int P=1004535809;
const int N=105;
int fact[N],invf[N],a[N];
int f[N][N][N];
int n;
int quick_power(int x,int y)
{
int ret=1;
for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
return ret;
}
void pre()
{
fact[0]=1;
for (int i=1;i<=n;++i) fact[i]=1ll*fact[i-1]*i%P;
invf[n]=quick_power(fact[n],P-2);
for (int i=n;i>=1;--i) invf[i-1]=1ll*invf[i]*i%P;
}
void dp()
{
f[0][0][0]=1;
for (int i=0;i<n;++i)
for (int j=0;j<=i;++j)
for (int k=0;k<=n-2;++k)
if (f[i][j][k])
{
(f[i+1][j][k]+=f[i][j][k])%=P;
for (int c=0;c<=n-2-k&&c<a[i+1];++c) (f[i+1][j+1][k+c]+=1ll*f[i][j][k]*invf[c]%P)%=P;
}
}
int main()
{
freopen("tree.in","r",stdin),freopen("tree.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%d",&a[i]);
pre(),dp();
printf("%d",n);
for (int i=2;i<=n;++i) printf(" %d",1ll*f[n][i][i-2]*fact[i-2]%P);
printf("\n");
fclose(stdin),fclose(stdout);
return 0;
}