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

Posted notnight

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 5136 2014icpc南京现场赛J 计数dp相关的知识,希望对你有一定的参考价值。

题目大意:给你一个树的直径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的主要内容,如果未能解决你的问题,请参考以下文章

2019.07.042018南京icpc现场赛

2018南京icpc现场赛心得

2018icpc南京现场赛-G Pyramid(打标找规律+逆元)

hdu 5956 The Elder 2016ACM/ICPC沈阳赛区现场赛I

2019南京ICPC(重现赛) F - Paper Grading

2014ACM/ICPC亚洲区鞍山赛区现场赛题解报告