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("\n"); } return 0; }