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

55 篇文章 0 订阅
32 篇文章 0 订阅

Description

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

Data Constraint

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

Solution

这是一道显然的数学题,而本人最讨厌数学题……
我们设出f[i][j][0..1]表示当前树的大小为i,最大匹配为j的情况。若根节点被选择,则为1,否则为0。那么我们考虑两种情况:
1、当前i仅有一个儿子。 因为要保证最大匹配,所以转移显然有 f[i+1][j][0]+=(i+1)f[i][j][1],f[i+1][j+1][1]+=(i+1)f[i][j][0]
2、当前i有两个儿子,大小分别为x,y,两个儿子的最大匹配分别为a,b。那么转移同样显然。 f[x+y+1][a+b+1][0]+=(x+y+1)f[x][a][1]f[y][b][1]Cxx+y2 Cxx+y 是因为存在两个点连边情况不同才被认为不同方案,所以我们要选出大小为x的子树放了哪些点。最后除以2是因为会出现重复的情况,即x,y调转角色。同时还有 f[x+y+1][a+b+1][1]+=(x+y+1)f[x][a][0]f[y][b][0]+Cxx+y2+(x+y+1)f[x][a][0]f[y][b][1]Cxx+y 。解释同上。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=55,mo=1e9+7;
ll f[maxn][maxn][2],c[maxn];
ll n,m,i,t,j,k,l,x,y,ans;
ll mi(ll x,ll y){
    if (y==1) return x;
    ll t=mi(x,y/2);
    if (y%2) return t*t%mo*x%mo;return t*t%mo;
}
ll dg(int x,int y){
    return c[x]*mi(c[y]*c[x-y]%mo,mo-2)%mo;
}
int main(){
//  freopen("data.in","r",stdin);
    scanf("%lld%lld",&n,&m);c[0]=1;
    for (i=1;i<=n;i++)
        c[i]=c[i-1]*i%mo;
    f[1][0][0]=1;
    for (i=2;i<=n;i++)
        for (j=0;j<=min(i/2,m);j++){
            f[i][j][0]=i*f[i-1][j][1]%mo;
            if (j) f[i][j][1]=i*f[i-1][j-1][0]%mo;
            for (x=1;x<i-1;x++)
                for (k=0;k<=min(x/2,j);k++){
                    y=i-1-x;
                    l=j-k;
                    if (y/2>=l) f[i][j][0]=(f[i][j][0]+i*f[x][k][1]%mo*f[y][l][1]%mo*dg(i-1,x)%mo*mi(2,mo-2))%mo;
                    l--;if (y/2<l || l<0)  continue;
                    f[i][j][1]=(f[i][j][1]+i*f[x][k][1]%mo*f[y][l][0]%mo*dg(i-1,x)%mo)%mo;
                    f[i][j][1]=(f[i][j][1]+i*f[x][k][0]%mo*f[y][l][0]%mo*dg(i-1,x)%mo*mi(2,mo-2))%mo;
                }
        }
    ans=(f[n][m][0]+f[n][m][1])%mo*mi(n,mo-2)%mo;
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值