1. 程式人生 > >HDU - 5136 2014icpc南京現場賽J 計數dp

HDU - 5136 2014icpc南京現場賽J 計數dp

first main sin cpc int rst class 深度 hdu

題目大意:給你一個樹的直徑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