HDU 5379 Mahjong tree

题目描述:

Description
Little sun is an artist. Today he is playing mahjong alone. He suddenly feels that the tree in the yard doesn’t look good. So he wants to decorate the tree.(The tree has n vertexs, indexed from 1 to n.)
Thought for a long time, finally he decides to use the mahjong to decorate the tree.
His mahjong is strange because all of the mahjong tiles had a distinct index.(Little sun has only n mahjong tiles, and the mahjong tiles indexed from 1 to n.)
He put the mahjong tiles on the vertexs of the tree.
As is known to all, little sun is an artist. So he want to decorate the tree as beautiful as possible.
His decoration rules are as follows:

(1)Place exact one mahjong tile on each vertex.
(2)The mahjong tiles’ index must be continues which are placed on the son vertexs of a vertex.
(3)The mahjong tiles’ index must be continues which are placed on the vertexs of any subtrees.

Now he want to know that he can obtain how many different beautiful mahjong tree using these rules, because of the answer can be very large, you need output the answer modulo 1e9 + 7.

Input
The first line of the input is a single integer T, indicates the number of test cases.
For each test case, the first line contains an integers n. (1 <= n <= 100000)
And the next n - 1 lines, each line contains two integers ui and vi, which describes an edge of the tree, and vertex 1 is the root of the tree.

Output
For each test case, output one line. The output format is “Case #x: ans”(without quotes), x is the case number, starting from 1.

Sample Input

2
9
2 1
3 1
4 3
5 3
6 2
7 4
8 7
9 3
8
2 1
3 1
4 3
5 1
6 4
7 5
8 4 

Sample Output

Case #1: 32
Case #2: 16 

题目分析:

给定一个n个结点的树,要求你给每个结点标记上1~n的数,使得
1、每一层的兄弟结点标号连续;
2、每一个子树的结点标记连续;
求有多少种标记方法(取模1e9+7)
据说这个是一道树形DP的题目,但是呃我没有推导出状态转移方程,只是在搜索的时候对结点的状态进行计数,通过公式得出的结论。
在分析的过程中,一层中若有超过两个的非叶子结点,无法标记。对于每一层的x个叶子结点,y个非叶子结点,x个叶子结点只需要全排列就行,也就是x!,非叶子结点y若等于1或2,我们要把这y个结点放在两端,如果y=1,放在左右两端两种情况,如果y=2,也是在左右两端的两个不同结点的2种情况,因此答案就算出来了。
总体来说这道题我们只需要记录树的形状,无所谓其标记的究竟是哪个数字(毕竟我们用全排列能算出来)。

其实还是很想知道这题DP公式的推导的,我觉得会很有趣。

代码如下:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int mod = 1000000007;
const int MAXN = 100010;


struct node
{
    int u,v;
    int next;
}mp[MAXN<<1];

int n;
int id;
int head[MAXN];
ll ans;
ll f[MAXN];
bool flag;

void init()
{
    memset(head,-1,sizeof(head));
    id=0;
    flag=true;
    ans=1;
}

void addedge(int u,int v)
{
    mp[id].u=u;
    mp[id].v=v;
    mp[id].next=head[u];
    head[u]=id++;
}

ll dfs(int u,int pre)
{
    if (!flag) return 0;
    ll sson=0,leap=0,son=1;//sson表示非叶子节点 leap表示叶子节点 son表示u及以下所有节点
    for(int i=head[u]; i!=-1; i=mp[i].next)
    {
        int v=mp[i].v;
        if (v==pre) continue;
        ll x=dfs(v,u);
        son+=x;
        if (x==1) leap++;
        if (x>=2) sson++;
    }
    if (sson>2) flag=false;//某一层非叶子结点个数大于2,无解
    else
    {
        ans=(ans*f[leap])%mod;//同一层满足连续
        if (sson!=0) ans=((ll)2*ans)%mod;//对非叶子结点进行处理
    }
    return son;
}

int main()
{
    int T;
    scanf("%d",&T);
    for(int t=1; t<=T; t++)
    {
        scanf("%d",&n);
        init();
        f[0]=1;
        for (int i=1; i<=n; i++)
            f[i]=(f[i-1]*(ll)i)%mod;  //全排列公式
        for(int i=1; i<n; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            addedge(u,v);//无向图 必须双向建边
            addedge(v,u);
        }
        dfs(1,-1);
        if (n>1) ans=((ll)2*ans)%mod;
        if (!flag) ans=0;
        printf("Case #%d: %lld\n",t,ans);
    }
    return 0;
}

你可能感兴趣的:(搜索)