码迷,mamicode.com
首页 > 其他好文 > 详细

HDU - 5136 2014icpc南京现场赛J 计数dp

时间:2018-06-02 22:24:16      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:icp   答案   nbsp   ==   long   条件   eof   name   first   

题目大意:给你一个树的直径k,要求每个点的度数不超过3, 问你有多少棵树满足条件。

 

思路:好难啊。 主要思想就是将一棵无根二叉树树划分成有根二叉树。

我们对k的分奇偶讨论:

我们定义dp[ i ] 为深度为 i 的有根二叉树的种数, sum 为 dp 的前缀和。

1.当k为偶数时,我们按直径的一般划分成2棵有根二叉树,两棵的深度都为 k / 2

答案由两部分组成, dp[k / 2] (两棵有根二叉树一样的情况)  + C(dp[k / 2], 2) (两棵二叉树不一样的情况)

 

2.当k为奇数时,我们可以划分成3棵有根二叉树, 其中两棵深度为 k / 2, 另一棵深度 <= k / 2

我们分为两大类来讨论, 第三棵树的深度为 k / 2 和 不是 k / 2, 方法和上述的差不多。

 

#include<bits/stdc++.h>
#define LL long long
#define fi first
#define se second
#define mk make_pair
#define pii pair<int,int>
#define piii pair<int, pair<int,int> >

using namespace std;

const int N = 1e5 + 10;
const int M = 10000 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-6;

LL dp[N], sum[N], ivn2, ivn6, k;
LL fastPow(LL a, LL b) {
    LL ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod; b >>= 1;
    }
    return ans;
}

void add(LL &a, LL b) {
    a += b; if(a >= mod) a -= mod;
}

LL f2(LL a) {
    if(a < 2) return 0;
    return a * (a - 1) % mod * ivn2 % mod;
}

LL f3(LL a) {
    if(a < 3) return 0;
    return a * (a - 1) % mod * (a - 2) % mod * ivn6 % mod;
}

void init() {
    dp[0] = 1, sum[0] = 1;
    dp[1] = 1; sum[1] = 2;
    ivn2 = fastPow(2, mod - 2);
    ivn6 = fastPow(6, mod - 2);

    for(int i = 2; i < N; i++) {
        dp[i] = dp[i - 1] * sum[i - 2] % mod;
        add(dp[i] ,f2(dp[i - 1]));
        add(dp[i] ,dp[i - 1]);

        sum[i] = sum[i - 1];
        add(sum[i], dp[i]);
    }
}


int main() {
    init();

    while(scanf("%lld", &k) != EOF && k) {
        if(k == 1) {
            puts("1");
        } else if(k & 1) {
            int depth = k / 2;
            LL ans = 0;
            add(ans, dp[depth]);
            add(ans, f2(dp[depth]) * 2 % mod);
            add(ans, f3(dp[depth]));

            add(ans, dp[depth] * sum[depth - 1] % mod);
            add(ans, f2(dp[depth]) * sum[depth - 1] % mod);
            printf("%lld\n", ans);
        } else {
            int depth = k / 2;
            LL ans = 0;
            add(ans, f2(dp[depth]));
            add(ans, dp[depth]);
            printf("%lld\n", ans);
        }
    }
    return 0;
}

/*
*/

 

HDU - 5136 2014icpc南京现场赛J 计数dp

标签:icp   答案   nbsp   ==   long   条件   eof   name   first   

原文地址:https://www.cnblogs.com/CJLHY/p/9126943.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!