Clarke and tree(prufer序列+dp)

Clarke and tree

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 134 Accepted Submission(s): 62

Problem Description

Clarke is a patient with multiple personality disorder. One day, Clarke turned into a CS, did a research on data structure.
Now Clarke has n nodes, he knows the degree of each node no more than ai a i . He wants to know the number of ways to choose some nodes to compose to a tree of size s(1sn) s ( 1 ≤ s ≤ n ) .

Input

The first line contains one integer T(1T10) T ( 1 ≤ T ≤ 10 ) , the number of test cases.
For each test case:
The first line contains an integer n(2n50) n ( 2 ≤ n ≤ 50 ) .
Then a new line follow with n numbers. The ith number ai(1ai<n) a i ( 1 ≤ a i < n ) denotes the number that the degree of the ith i t h node must no more than ai a i .

Output

For each test case, print a line with n n integers. The ith number denotes the number of trees of size i i modulo 109+7.

Sample Input
1
3
2 2 1
Sample Output
3 3 2
Hint:

At first we know the degree of node 1 can not more than 2, node 2 can not more than 2, node 3 can not more than 1. So
For the trees of size 1, we have tree ways to compose, are 1, 2 and 3. i.e. a tree with one node.
For the trees of size 2, we have tree ways to compose, are 1-2, 1-3, 2-3.
For the trees of size 3, we have two ways to compose, are 1-2-3, 2-1-3.
传送门!!
n n 个点,第i个点的限制为度数不能超过 ai a i
现在对于每一个 s(1sn) s ( 1 ≤ s ≤ n ) ,问从这 n n 个点中选出s个点组成有标号无根树的方案数。






解:

我们首先知道prufer序列。在所选节点定下来之后,每一个长度为n的prufer序列对应且唯一对应一棵节点为n+2的树,而且有一个很好的性质,就是说所有的prufer序列和所有的树是一一对应的。也就是说,求树的计数和prufer序列计数是等价的。

那么这道题我们就有一个很好的想法: fi,j f i , j 表示选到第 i i 个点,prufer序列长度为j的方案数有多少。转移是 fi+1,j+k+=fi,jCjj+k f i + 1 , j + k + = f i , j ∗ C j + k j
为什么呢?我们现在有一个长度为 x x 的prufer序列,我们向里面加入k个点的方案有多少种?由于这 k k 个点是相同的,所以我们可以换一种思路:现在已经有k+x个点了,我们在中间选 x x 个位置,把原来的序列排进去。方案数是等价的。

但是这样写写发现是wa的,只有选出n个点组成数的时候是对的。是为什么呢?在看一看上面的描述:在所选节点定下来之后,每一个prufer序列才唯一对应一棵树。什么意思?我们现在有5个点可以选,而prufer序列是1,2。剩下在树里的两个点可能是3,4,5里选两个。由此可见这个prufer序列对应的树实际上有3个。而我们的dp方程只是prufer序列计数,并没有记录树的个数。
我们用 fi,j,k f i , j , k 表示选到前 i i 个点,选了j个点且prufer序列长度为 k k 的方案数。值得注意的是选了某一个点并不代表它在prufer序列中,因为我们如果选这个点度数为1的话,它并不会出现在prufer序列中。
转移:枚举下一个点的度数(d)
如果 d=0 d = 0 ,表示不把这个点放进prufer序列中, fi+1,j,k+=fi,j,k f i + 1 , j , k + = f i , j , k
如果 d0 d ≠ 0 ,表示把这个点选入prufer序列中, fi+1,j+1,k+d1+=fi,j,kCkk+d1 f i + 1 , j + 1 , k + d − 1 + = f i , j , k ∗ C k + d − 1 k d=1 d = 1 时相当于放0个点到prufer序列中。
这样我们dp状态就记录了这个状态下的树的个数。因为dp中计算了不在prufer序列中的点,所以没问题了。
下面贴代码,有问题可以看看转移方程:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int n,T; 
long long C[105][105];
long long const mod=1000000007;
long long f[105][105][105];
long long a[105];

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
          scanf("%lld",&a[i]);
        for(int i=0;i<=n;i++)
          C[i][0]=1,C[i][i]=1;
        for(int i=2;i<=n;i++)
          for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
        cout<<C[5][3]<<endl;
        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++)
              for(int d=0;d<=a[i+1];d++)
                if(k+d<=n)
                {
                  if(d==0) f[i+1][j][k]=(f[i+1][j][k]+f[i][j][k])%mod;
                  else f[i+1][j+1][k+d-1]=(f[i+1][j+1][k+d-1]+f[i][j][k]*C[k+d-1][k])%mod;
                }
        printf("%d",n);
        for(int i=2;i<=n;i++)
          printf(" %lld",f[n][i][i-2]);
        printf("\n");
    }
}

丧病oj输出居然不去除行末空格。居然以前交的时候没发现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值