1. 程式人生 > >HDU-5136 2014ICPC廣州現場賽J - Yue Fei's Battle

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;
}