【NOIP2014八校联考第4场第2试10.20】准备复赛

Description

今年的NOIP初赛真是简单,小可可不用吹灰之力就考进了复赛,但是复赛可没有那么简单了,小可可想要好好准备复赛,争取复赛拿个省一。今天小可可在复习树和图的最大匹配时就碰到这样的一个难题:n个节点满足以下性质的不同的树有多少种。
1、树是有标号的,每个节点被标上1到n之间的整数;
2、每个节点最多和其他3个节点相连,但是1号节点最多和其他2个节点相连;
3、这棵树的最大匹配(把树看成二分图后的最大匹配)数为k。
两棵树被认为不同当且仅当存在两个点u、v,在一棵树中u、v之间有边,另一棵树中u、v之间没边。
由于答案可能很大,所以小可可让你输出答案模1000000007 (10^9 + 7)。

Solution

据题意,这个树是一个二叉树。
一看就知道是DP题,最重要的就是DP的状态的设置。
设f[i][j][0,1]表示用了i个数,然后有j个匹配,根节点是否被匹配。
当i只有一个儿子的时候:
f[i][j][0]=f[i1][j][1]i,f[i][j][1]=f[i1][j][1]i
因为每个节点都可能作为根节点,所以要*i。
当i有两个儿子的时候:
f[i][j][0]+=(f[x][k][1]+f[y][o][1])(x+y+1)Cxi12
f[i][j][1]+=(f[x][k][1]f[y][o1][0]+f[x][k][0]f[y][o1][0]2)iCxi1)
要乘以i是因为每个节点都有可能当做根节点,乘以 Cxi1 是因为要分配左边的方案,当左右是零和左右是一的时候要除以2。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int mo=1e9+7;
typedef long long ll;
const int maxn=57*57;
ll i,j,k,l,t,n,m,x,y,o,kk;
ll c[107][107],ni2,ans;
ll f[57][57][2];
ll qsm(ll x,ll y){
    ll z=1;
    for(;y;y/=2,x=x*x%mo)if(y&1)z=z*x%mo;
    return z;
}
int main(){
    ni2=500000004;
    fo(i,1,100)c[i][0]=c[0][i]=1;
    fo(i,1,100)fo(j,1,i)c[i][j]=(c[i-1][j-1]+c[i-1][j])%mo;c[0][0]=1;
    scanf("%lld%lld",&n,&kk);
    f[1][0][0]=1;
    fo(i,2,n){
        fo(j,0,min(kk,i/2)){
            f[i][j][0]=f[i-1][j][1]*i%mo;
            if(j)f[i][j][1]=f[i-1][j-1][0]*i%mo;
            fo(x,1,i-1){
                fo(k,0,min(j,x/2)){
                    y=i-x-1;o=j-k;
                    if(y/2>=o)f[i][j][0]=(f[i][j][0]+f[x][k][1]*f[y][o][1]%mo*i%mo*c[i-1][x]%mo*ni2%mo)%mo;
                    if(y/21)continue;
                    if(o>=1)f[i][j][1]=(f[i][j][1]+(f[x][k][1]*f[y][o-1][0]%mo+f[x][k][0]*f[y][o-1][0]%mo*ni2%mo)%mo*i%mo*c[i-1][x]%mo)%mo;
                }
            }
        }
    }
    ans=(f[n][kk][0]+f[n][kk][1])%mo*qsm(n,mo-2)%mo;
    printf("%lld\n",ans);
}

你可能感兴趣的:(noip,DP)