codeforces 724F. Uniformly Branched Trees

来源:互联网 发布:知乎怎样匿名回答 编辑:程序博客网 时间:2024/06/06 10:04

题目链接:http://codeforces.com/problemset/problem/724/F
题目大意:给定三个数n,d,mod,求有多少种n个点的不同构的树满足:除了度数为1的结点外,其余结点的度数均为d。答案对质数mod取模。
数据范围:1 ≤ n ≤ 1000, 2 ≤ d ≤ 10, 10^8 ≤ mod ≤ 10^9

题解:看到题目不难想到是树形dp。首先我们考虑有根树,f[i][j][k]表示有 i 个结点,根结点有 j 棵子树,每棵子树大小不超过k的方案数。有两种情况:
1.所有的子树大小都小于k,转移到f[i][j][k-1]。
2.有 l (l>0)棵子树大小为k,结果应为f[i-l*k][j-l][k-1]*C(f[k][d-1][k-1]+l-1,l)。注:C(X+l-1,l)表示从X种方案中选取 l 种,可以重复。
求出了有根树的方案数,我们需要转化成无根树。上述方案会重复计算答案的值,是因为同一棵树选取不同的结点作根时在 f 中表现出的状态是不同的,所以我们应该找到一个特殊的结点作为根,即树的重心,因为树的重心只有一个或两个。我们知道,对于一棵树的重心,它的子树大小不会超过n/2,所以答案为f[n][d][n/2]。而当一棵树有两个重心时,说明这棵树的重心的最大子树恰好有n/2(n为偶数)个点,此时应该减去重复(即两个重心在 f 中表示的状态不同时)的方案数C(f[n/2][d-1][n/2],2)。
最后,当n<=2时没有度数为d的点,此时特判一下就可以了。
时间复杂度O(n^2*d^2)

代码如下:

#include <algorithm>#include <cstdio>int f[1005][11][1005],g[1005][11],ine[11],n,d,mo;int inv(int x){    int i=1,y=mo-2;    for (;y;y>>=1,x=1ll*x*x%mo)        if (y&1) i=1ll*i*x%mo;    return i;}int main(){    scanf("%d%d%d\n",&n,&d,&mo);    for (int i=0;i<=n;i++) f[1][0][i]=1;    for (int i=1;i<=d;i++) g[1][i]=1;    for (int i=1;i<=d;i++) ine[i]=inv(i);    for (int i=2;i<=n;i++)    {        for (int j=1;j<=d;j++)        {            for (int k=1;k<i;k++)                for (int l=1;l<=j && l*k<=i;l++)                    f[i][j][k]=(f[i][j][k]+1ll*f[i-l*k][j-l][k-1]*g[k][l])%mo;            for (int k=1;k<=n;k++) f[i][j][k]=(f[i][j][k]+f[i][j][k-1])%mo;        }        g[i][1]=f[i][d-1][n];        for (int j=2;j<=d;j++)            g[i][j]=1ll*g[i][j-1]*(f[i][d-1][n]+j-1)%mo*ine[j]%mo;    }    int ans;    if (n<=2) ans=1;        else ans=f[n][d][n/2];    if (n>2 && !(n&1))        ans=(ans-1ll*(f[n/2][d-1][n/2]-1)*f[n/2][d-1][n/2]%mo*ine[2]%mo+mo)%mo;    printf("%d\n",ans);}
0 0
原创粉丝点击