[jzoj]3874. 【NOIP2014八校联考第4场第2试10.20】准备复赛(exam)(树形DP+组合数)

来源:互联网 发布:正当防卫3内存优化补丁 编辑:程序博客网 时间:2024/06/06 08:26

Problem

求n个节点满足以下性质的不同的树有多少种。

1、树是有标号的,每个节点被标上1到n之间的整数;
2、每个节点最多和其他3个节点相连,但是1号节点最多和其他2个节点相连;
3、这棵树的最大匹配(把树看成二分图后的最大匹配)数为k。

两棵树被认为不同当且仅当存在两个点u、v,在一棵树中u、v之间有边,另一棵树中u、v之间没边。

输出方案数 mod 1e9+7

Data Constraint

对于30%的数据,2≤n≤5;
对于60%的数据,2≤n≤20;
对于100%的数据,2≤n≤50。

Solution

  • 我们设f[i][j][0/1]表示当前由i个节点组成的一棵树,最大匹配为j,根节点是否选择.

  • 我们知道,根节点是否选择,所影响的最大匹配不会超过1,且选根节点,一定比不选优.

  • 那么因为此题的一个重要性质是:

    • 该树是二叉树
  • 所以我们可以考虑经典的二叉树DP方法:

  • 枚举整棵树的大小及左子树的大小,从而推出右子树的大小,转移.

  • 设左子树的状态为f[x][T1][0/1],右子树即为f[y][T2][0/1],满足x+y=n1

  • 那么我们可以通过“根节点是否选择,所影响的最大匹配不会超过1”来得出f[i][S][0/1]中的S.

  • 并进行转移,转移的时候需要注意的是“顺序”.

  • 因为这n个节点组成的树,树的节点是有顺序的,所以我们要乘上一个左子树的方案数(C(i1,x)种),以及根有多少种可能(i种)

  • 但需要注意一点就是,当x=y时,我们任意的一种方案,左子树和右子树都会相同,所以我们要除以2.

  • 具体实现看标

#include <iostream>#include <cstdio>#include <cstring>#define ll long longconst ll Maxn = 100, mo = 1e9 + 7;using namespace std;ll n,k,x[Maxn],y[Maxn],pre[Maxn],jc[Maxn],ny[Maxn],ans;ll f[Maxn][50][2];ll C(ll x,ll y){    return ((jc[x] * ny[y] % mo) * ny[x-y]) % mo;}ll ksm(ll x,ll y){    ll sum = 1;    while (y)    {        if (y & 1) sum = (sum * x) % mo;        x = (x * x) % mo;        y = y / 2;    }    return sum;}int main(){    scanf("%lld%lld",&n,&k), jc[0] = ny[0] = 1;    for (ll i=1;i<=n;i++)        jc[i] = (jc[i-1] * i) % mo, ny[i] = ksm(jc[i],mo-2);    f[0][0][0] = f[1][0][0] = 1;    for (ll i = 2; i <= n; i++)    {        for (ll x = i / 2; x < i; x++)        {            ll y = i - x - 1, maxer1 = x / 2, maxer2 = y / 2, s1 = (C(i-1,x) * i) % mo, s2;            if (x==y) s2 = ksm(2,mo-2); else s2 = 1;            for (ll T1 = 0; T1 <= maxer1; T1++)            {                if (f[x][T1][0]+f[x][T1][1]==0) continue;                if (y == 0)                {                    f[i][T1+1][1] = (f[i][T1+1][1] + (i * f[x][T1][0]) % mo );                    f[i][T1]  [0] = (f[i][T1]  [0] + (i * f[x][T1][1]) % mo );                    continue;                }                for (ll T2 = 0; T2 <= maxer2; T2++)                {                    if (f[y][T2][0]+f[y][T2][1]==0) continue;                    f[i][T1+T2+1][1] = (f[i][T1+T2+1][1] + (((f[x][T1][0] * f[y][T2][1]) % mo) * ((s1 * s2) % mo) ));                    f[i][T1+T2+1][1] = (f[i][T1+T2+1][1] + (((f[x][T1][1] * f[y][T2][0]) % mo) * ((s1 * s2) % mo) ));                    f[i][T1+T2+1][1] = (f[i][T1+T2+1][1] + (((f[x][T1][0] * f[y][T2][0]) % mo) * ((s1 * s2) % mo) )) % mo;                    f[i][T1+T2]  [0] = (f[i][T1+T2  ][0] + (((f[x][T1][1] * f[y][T2][1]) % mo) * ((s1 * s2) % mo) )) % mo;                }            }        }    }    printf("%lld",((f[n][k][0] + f[n][k][1]) * (ksm(n,mo-2))) % mo);}
  • 遇到这种类型的题目,要求构出一颗二叉树的,满足什么条件,一般都分左子树,和右子树来转移

  • 一定要注意组合数.

阅读全文
0 0
原创粉丝点击