【GDSOI2017第二轮模拟】树
Description
有n个点,它们从1到n进行标号,第i个点的限制为度数不能超过A[i].
现在对于每个s (1 <= s <= n),问从这n个点中选出一些点组成大小为s的有标号无根树的方案数。
Input
第一行一个整数n.
第二行n个整数表示A[i].
Output
输出一行n个整数,第i个整数表示s=i时的答案。答案模1004535809 = 479 * 221+ 1。
Sample Input
3
2 2 1
Sample Output
3 3 2
Data Constraint
20%的数据:n <= 6
60%的数据:n <= 50
100%的数据:n <= 100
反思&题解
比赛&正解思路: 第一眼看到题,woc做过原题,这题白给了首先要想做这道题要知道一个知识点叫purfer序列,对于一棵大小为x的无根树,我们需要生成一个长度为
的purfer序列(当然大小为1的树答案就是n了),而每个节点的度数就是这个数在数列中出现的次数
设
表示从前i个节点中取j个节点形成长度为k的purfer序列,转移方程很显然是
注意注意一定要用杨辉三角与处理好组合数,如果在循环里面直接用组合数就会多带一个log爆掉(因为用费马小定理会有一个快速幂)我就是这样没了30……
反思: 曾经做过的题不切就不对了呀
CODE
#include<bits/stdc++.h>
using namespace std;
const long long mo=1004535809;
long long f[105][105][105],a[105],n,c[105][105];
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%lld",&n);
long long i,j;
for (i=1;i<=n;i++)
scanf("%lld",&a[i]);
for (i=0;i<=n;i++)
c[i][0]=1;
for (i=1;i<=n;i++)
{
for (j=1;j<=n;j++)
c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
}
f[0][0][0]=1;
long long k,v;
for (i=1;i<=n;i++)
{
for (j=0;j<=i;j++)
{
for (k=0;k<=n-2;k++)
{
f[i][j][k]=f[i-1][j][k];
if (j)
{
for (v=0;v<=min(a[i]-1,k);v++)
f[i][j][k]=(f[i][j][k]+f[i-1][j-1][k-v]*c[k][v])%mo;
}
}
}
}
printf("%lld ",n);
for (i=2;i<=n;i++)
printf("%lld ",f[n][i][i-2]);
printf("\n");
return 0;
}