HDU 5379 Mahjong tree (DFS)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5379

题意:给出一棵n个节点的树,你在上面放n个麻将,麻将可以从小到大排序编号1-n,要使得:1)每个节点上只有一个麻将,2)父节点相同的节点上麻将的编号连续,3)一棵子树(包括根节点)上的全部麻将,编号为连续,求方案数。

思路:

1)确定了父节点上麻将的编号后,如果子节点有非叶节点,非叶节点的数量不能超过2,否则不能同时满足条件(2)(3);

2)dp[fa] 记录确定 fa 点放的麻将编号后以 fa 为根的树的方案数,计算 dp[fa] 必然需要用1 * dp[son1] * dp[son2] * ...

3)fa 如果有非叶节点,非叶节点为根的大小为b子树,取的编号必然是可取的a个连续编号的左边b个或右边b个,方案数*2;

4)fa 如果有叶节点c个,方案数 * c 的全排列

时间复杂度是n,但是会爆栈,要加 #pragma comment(linker, "/STACK:102400000,102400000")  C++交

代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int MOD = 1000000007;
int n, flag;
ll A[100005];
vector<int> G[100005];

ll dfs(int fa, int u)
{
    if(!flag) return 0;
    ll res = 1;
    int cnt = 0, cntleaf = 0; // cnt为子节点个数,cntleaf为叶节点个数
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(v != fa)
        {
            ll temp = dfs(u, v);
            if(!flag) return 0;
            cnt++;
            if(G[v].size() == 1) cntleaf++;
            res = res * temp % MOD;
        }
    }
    if(cnt - cntleaf > 2)
    { //非叶节点数大于2
        flag = 0; return 0;
    }
    if(cntleaf)
        res = res * A[cntleaf] % MOD;
    if(cnt - cntleaf) //有非叶节点
        res = res * 2 % MOD;
    return res;
}


int main()
{
    #ifdef LOCAL
    freopen("data.in", "r", stdin);
    #endif

    A[0] = 1; A[1] = 1;
    for(int i = 2; i <= 100000; i++)
        A[i] = A[i - 1] * i % MOD;
    int t, a, b;
    scanf("%d", &t);
    for(int cas = 1; cas <= t; cas++)
    {
        scanf("%d", &n);
        if(n == 1)
        {
            printf("Case #%d: 1\n", cas);
            continue;
        }
        for(int i = 0; i <= n; i++)
            G[i].clear();
        for(int i = 1; i < n; i++)
        {
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        flag = 1;
        ll ans = dfs(-1, 1);
        if(flag)
            printf("Case #%d: %I64d\n", cas, ans * 2 % MOD); //节点1放的麻将编号取最大或最小值
        else
            printf("Case #%d: 0\n", cas);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值