标签: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