HDU 5629 Clarke and tree dp+prufer序列

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=562

题意:

求给每个节点的度数允许的最大值,让你求k个节点能组成的不同的生成树个数。

题解:

对于n个节点形成的一颗生成树,有一个与之唯一对应的大小为n-2的prufer数列。

并且一个节点的度数减一为它出现在prufer数列中的次数。

那么我们求生成树的个数可以转化为求prufer数列的可重集排列,而这个可以用dp来做。

dp[i][j][k]表示处理到第i个节点,已经用了j个节点,且可重集大小为k的排列组合数。

代码:

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

const int maxn = 55;
const int mod = 1e9 + 7;
typedef long long LL;

int n;
LL dp[maxn][maxn][maxn];
int arr[maxn];

LL C[maxn][maxn];
void pre() {
    C[0][0] = 1;
    for (int i = 1; i < maxn; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++) {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
}

void init() {
    memset(dp, 0, sizeof(dp));
}

int main() {
    pre();
    int tc;
    scanf("%d", &tc);
    while (tc--) {
        init();
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
        }
        dp[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++) {
                    //不用第i+1个数
                    dp[i + 1][j][k] = (dp[i + 1][j][k] + dp[i][j][k]) % mod;
                    //用第i+1个数
                    for (int l = 1; l <= arr[i + 1] && l - 1 + k <= n - 2; l++) {
                        dp[i + 1][j + 1][k + l - 1] = (dp[i + 1][j + 1][k + l - 1] + C[k + l - 1][l - 1] * dp[i][j][k]) % mod;
                    }
                }
            }
        }
        printf("%d", n);
        for (int i = 2; i <= n; i++) printf(" %lld", dp[n][i][i - 2]);
        printf("
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/fenice/p/5551490.html