jzoj1794 保镖排队 (树形dp)

保镖排队 (Standard IO)

Description

【问题背景】
  教主LHX作为知名人物,时刻会有恐怖分子威胁他的生命。于是教主雇佣了一些保镖来保障他的人生安全。

【题目描述】
  教主一共雇佣了N个保镖,编号为1~N。每个保镖虽然身手敏捷武功高强,但是他在其余N-1个保镖里,都会有一个“上司”,他会对他的上司言听计从。但一号保镖例外,他武功盖世,不惧怕其余任何保镖,所以他没有上司。
  教主LHX会对这N个保镖进行定期视察。每次视察的时候,首先会让所有保镖排队。
  对于每个保镖,在他心目中会对他的所有下属的武功实力排个队。
  现在教主要求排出来的队伍满足:①互为上司-下属的两个保镖,上司在前,下属在后 ②对于一个保镖的所有下属,武功实力较强的在前,较弱的在后。
  教主想知道,总的排队方法数除以10007的余数是多少。

Input

  输入的第一行为一个正整数T,表示了数据组数。
  对于每组数据:
  第一行为一个正整数N。
  接下来N行,每行描述一个保镖。
  第i+1行,会有一个整数K,代表第i个保镖的下属个数,接下来K个数,代表第i个保镖的下属按照武功实力从高到低的编号。

Output

  输出包括C行,每行对于每组数据输出方案数mod 10007后的结果。

Sample Input

2
5
2 2 3
2 4 5
0
0
0
7
2 2 3
2 4 5
2 6 7
0
0
0
0

Sample Output

3
10

代码

#include <cstdio>
#include <cstring>
#include <string>
#define mo 10007
#define N 1005
using namespace std;


int size[N],f[N],n,t,l;
int jc[N],ny[N];
int a[N][N];

int ksm(int x,int y)
{
    int ret=1;
    while (y)
    {
        if (y&1) ret=ret*x%mo;
        x=x*x%mo;
        y/=2;
    }
    return ret;
}

int C(int x,int y){return jc[x]*ny[x-y]%mo*ny[y]%mo;}

void dfs(int x)
{
    f[x]=size[x]=1;
    int tot=0;
    for (int i=a[x][0];i>=1;i--)
    {
        int y=a[x][i];
        dfs(y);
        tot+=size[y];
        f[x]=(((f[x]*f[y])%mo)*C(tot-1,size[y]-1))%mo;
    }
    size[x]+=tot;
}

int main()
{
    scanf("%d",&t);
    jc[0]=ny[0]=1;
    for (int i=1;i<=1000;i++)
    {
        jc[i]=jc[i-1]*i%mo;
        ny[i]=ksm(jc[i],mo-2);
    }
    while (t--)
    {
        memset(f,0,sizeof(f));
        memset(size,0,sizeof(size));
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&a[i][0]);
            for (int j=1;j<=a[i][0];j++)
                scanf("%d",&a[i][j]);
        }
        dfs(1);
        printf("%d\n",f[1]);
    }
}

猜你喜欢

转载自blog.csdn.net/zhanghaoxian1/article/details/79283812
今日推荐