HDU 5379 树形DP Mahjong tree
任意一棵子树上节点的编号连续,每个节点的所有二字节点连续,求编号方案的总数。
稍微分析一下可知
- 每个节点的非叶子节点个数不能多于两个,否则这个子树无解,从而整棵树都无解。
- 每棵子树将所有节点按照编号从小到大排序,根节点要么在最左端,要么在最右端,而且这两种情况相等。(后面会有具体分析)
设size(u)表示以节点u为根的子树中节点总数。
d(u)表示用1 ~ size(u)给以u为根的子树编号的合法方案数,考虑下面几种情况:
①: u是叶子节点,方案数为1.
②: u的所有儿子节点都是叶子节点,那么所有儿子节点连续(假设儿子节点有S个),u只能放在所有儿子节点的前面或者最后面。所以方案数就是2(S!),因为儿子节点之间互不影响可以任意排列。
③: u只有一个非叶儿子节点v。
方案数为:2 * d(v) * S!
首先子树v的排列方案有d(v)种,子树v的排列确定下来以后,而且v一定是在这个排列的某一端,由于所有儿子节点连续,所以这S个v的叶子兄弟必须紧挨着v,所以有S!种,此时只有u的位置没有确定好,u可以放在排列的开头或者末尾,所以答案最终要乘2.
④: u有两个非叶子儿子节点v1, v2.
首先u的所有儿子节点是要连续的,所以v1, v2和他俩的叶子兄弟要在连续的一段;而且还要满足,v1这棵子树连续,v2这棵子树连续,所以v1和v2注定只能排在在u的儿子中的两端。
排列大概就是这样的,因为v1排在左端的方案数为d(v1) / 2,同样地v2排在右端的方案数为d(v2) / 2,叶子兄弟在v1和v2之间任意排列,u排在左右两端都行。
所以上图表示总的方案数为 d(v1) / 2 * d(v2) / 2 * S! * 2 = d(v1) * d(v2) / 2 * S!
不要急,还有一半情况没考虑到。就是v1子树排在右边,v2子树排在左边,所以上面的答案还要乘个2,得到 d(v1) * d(v2) * S!
⑤: 非叶子节点数多于两个,开头已经说过了,无解。
因为数据比较大,要手动扩栈然后用C++提交。
1 #pragma comment(linker, "/STACK:102400000,102400000") 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #include <vector> 7 using namespace std; 8 9 typedef long long LL; 10 11 const int maxn = 100000 + 10; 12 const LL MOD = 1000000007; 13 14 vector<int> G[maxn]; 15 16 int sz[maxn]; 17 LL d[maxn], fac[maxn]; 18 19 void dfs(int u, int fa) 20 { 21 sz[u] = 1; 22 LL T = 1; 23 int c1 = 0, c2 = 0; 24 for(int i = 0; i < G[u].size(); i++) 25 { 26 int v = G[u][i]; 27 if(v == fa) continue; 28 dfs(v, u); 29 sz[u] += sz[v]; 30 31 if(d[v] == 0) { d[u] = 0; return ; } 32 33 if(sz[v] > 1) { c1++; T = (T * d[v]) % MOD; } 34 else c2++; 35 36 if(c1 > 2) { d[u] = 0; return ; } 37 } 38 39 if(sz[u] == 1) { d[u] = 1; return ; } 40 if(c1 < 2) d[u] = (2LL * fac[c2] * T) % MOD; 41 else d[u] = (fac[c2] * T) % MOD; 42 } 43 44 int main() 45 { 46 fac[0] = 1; 47 for(int i = 1; i < maxn; i++) fac[i] = (fac[i - 1] * i) % MOD; 48 49 int T; scanf("%d", &T); 50 for(int kase = 1; kase <= T; kase++) 51 { 52 int n; scanf("%d", &n); 53 for(int i = 1; i <= n; i++) G[i].clear(); 54 for(int i = 1; i < n; i++) 55 { 56 int u, v; scanf("%d%d", &u, &v); 57 G[u].push_back(v); 58 G[v].push_back(u); 59 } 60 61 printf("Case #%d: ", kase); 62 63 if(n == 1) { puts("1"); continue; } 64 65 dfs(1, 0); 66 printf("%I64d\n", d[1]); 67 } 68 69 return 0; 70 }