HDU-5136 2014ICPC广州现场赛J - Yue Fei's Battle

题目链接

求直径为k、每个点的度不超过3的不同构树的数目。

考虑按照直径所在的链分为若干部分,显然每部分都是一棵二叉树。dp[i]为深度为i的不同构树的数目,sum[i]为num[i]的前缀和。

对于深度为i时,根的两个分支有可能为:

(1)一个深度为i-1,另一个深度小于i-1,有dp[i-1]*sum[i-2]种方案。

(2)深度都为i-1,两分支不同时为dp[i-1]*(dp[i-1]-1)/2种,相同时为dp[i-1]种。

即:dp[i]=dp[i-1]*sum[i-2]+dp[i-1]+C(dp[i-1],2)

考虑直径为k时最终的答案。可以有如下的讨论:

k为偶数时,两边的树的深度都为k/2,显然此时答案即为dp[i]+C(dp[i],2)

k为奇数时,中间的点上有3个分支,而且这之中最少有两个分支的深度为k/2。

(1)另外一个的深度小于k/2时:sum[i-1]*(dp[i]+C(dp[i],2))

(2)3个分支深度都为k/2:

都相同:dp[i],两个相同:dp[i]*(dp[i]-1),互不相同:C(dp[i],3)

即:k为奇数的答案为sum[i-1]*(dp[i]+C(dp[i],2))+dp[i]+dp[i]*(dp[i]-1)+C(dp[i],3)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;

const int maxn = 100050;
const ll mod = 1e9 + 7;

int n;
ll dp[maxn], num[maxn], sum[maxn];

ll qpow(ll a, ll x)
{
    ll res = 1;
    while(x)
    {
        if(x & 1) res = (res*a) % mod;
        a = (a*a) % mod;
        x >>= 1;
    }
    return res;
}

void init()
{
    ll inv2 = qpow(2, mod - 2), inv6 = qpow(6, mod - 2);
    num[0] = num[1] = sum[0] = 1, num[2] = sum[1] = 2, sum[2] = 4;
    for(int i = 3;i < maxn;i++)
    {
        num[i] = (num[i-1] + 1)*num[i-1]%mod*inv2%mod;
        num[i] = (num[i] + num[i-1]*sum[i-2]%mod) % mod;
        sum[i] = (sum[i-1] + num[i]) % mod;
    }
    dp[1] = dp[2] = 1;
    for(int i = 3;i < maxn;i++)
    {
        ll tmp = num[i/2]*(num[i/2] + 1)%mod*inv2%mod;
        if(i % 2 == 0) dp[i] = tmp;
        else
        {
            dp[i] = sum[i/2 - 1]*tmp%mod;
            dp[i] = (dp[i] + num[i/2]*(num[i/2]-1+mod)%mod + num[i/2]) % mod;
            dp[i] = (dp[i] + num[i/2]*(num[i/2]-1+mod)%mod*(num[i/2]-2+mod)%mod*inv6%mod) % mod;
        }
    }
}

int main()
{
    init();
    while(scanf("%d", &n))
    {
        if(n==0)  break;
        printf("%lld\n", dp[n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/NPU_SXY/article/details/82962239
yue
今日推荐