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 }
    代码君
  • 相关阅读:
    ZOJ 2601 Warehouse Keeper
    POJ 2175 Evacuation Plan
    NYIST 1108 最低的惩罚
    二进制 与 十进制 互转
    javascript学习(9)——[设计模式]单例
    2013 Changsha Regional 一样的木板一样的气球
    muduo简化(1):Reactor的关键结构
    Oracle 11g New 热补丁
    跳转表实例
    [置顶] android 心跳包的分析
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4729900.html
Copyright © 2011-2022 走看看