HDU 5629 Clarke and tree(Purfer序列+dp+组合数学)

256 篇文章 0 订阅
202 篇文章 1 订阅

Description
给出n个正整数ai,现从n个点中选s个点构成一棵树,使得编号为i的节点度数不超过ai,问所有可能情况数
Input
第一行一整数T表示用例组数,每组用例首先输入一整数n,之后n个整数ai
(1<=T<=10,2<=n<=50,1<=ai < n)
Output
对于每组用例,输出n个数,第s个数表示节点数为s的合法的树的数量,结果模1e9+7(1<=s<=n)
Sample Input
1
3
2 2 1
Sample Output
3 3 2
Solution
知道点的度数求可以构造的树的数量一般用到Purfer序列,因为一个长度为n-2的Purfer序列唯一对应一个n个点的树,且Purfer序列中i出现的次数就是节点i的度数减一,此题只给出点度数的上限,所以可以考虑dp
用dp[i][j][j]表示从前i个点中选取j个点且这j个点在Purfer序列中出现k次的合法Purfer序列数(也就是合法树的数量),考虑第i个点是否被选,以及被选后在Purfer序列出现的次数有以下转移方程:
dp[i][j][k]+=dp[i-1][j][k](不选i)
dp[i][j+1][k+d]+=C[k+d][d]*dp[i-1][j][k],d=0,1,…,a[i]-1(选i,出现d次,组合数C(k+d,d)表示在一个已经有序的长度为k的序列中插入d个相同的数的方案数)
dp[n][i][n-2]即为答案(1<=i<=n)
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 1000000007ll
#define maxn 55
int T,n,a[maxn];
ll C[maxn][maxn],dp[maxn][maxn][maxn];
void init()
{
    memset(C,0,sizeof(C));
    C[0][0]=1;
    for(int i=1;i<=50;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)C[i][j]=(C[i][j-1]+C[i-1][j-1])%mod;
    }
}
int main()
{
    init();
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        dp[0][0][0]=1;
        for(int i=1;i<=n;i++)
            for(int j=0;j<i;j++)
                for(int k=0;k<=n-2;k++)
                {
                    dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k])%mod;
                    for(int d=0;d<a[i]&&k+d<=n-2;d++)
                        dp[i][j+1][k+d]=(dp[i][j+1][k+d]+C[k+d][d]*dp[i-1][j][k]%mod)%mod;
                }
        printf("%d",n);
        for(int i=2;i<=n;i++)printf(" %I64d",dp[n][i][i-2]);
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值