zoukankan      html  css  js  c++  java
  • 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
    ", d[1]);
    67     }
    68 
    69     return 0;
    70 }
    代码君
  • 相关阅读:
    垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
    自考感悟,话谈备忘录模式
    [每日一题] OCP1z0-047 :2013-07-26 alter table set unused之后各种情况处理
    Java实现 蓝桥杯 算法提高 p1001
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 因式分解
    Java实现 蓝桥杯 算法提高 因式分解
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4729900.html
Copyright © 2011-2022 走看看