【JZOJ5165】小W的动漫

Description

给出一棵以1为根的树,定义一棵树的一种序列,该序列满足:每个节点的所有儿子都要排在该节点后面,且每个节点的每个儿子,左边的要排在右边的前面(输入按顺序给出),求序列的可能方案数。模10007。

Solution

考虑转换模型,左边的儿子要比右边的儿子放在前面,那么让左边的儿子成为右边儿子的父亲,构出来一棵二叉树,那么现在求的就是满足父亲比儿子先遍历的遍历整棵树的方案数。
Fi 表示以i为根的子树的方案数, Fl 表示左儿子的, Fr 表示右儿子。
那么现在左边有 sizel 个点,走的方案为 Fl ,右边同理。
因为左右子树互不影响,所以两边可以交错着走,那么方案为: Csizersizel+sizerFlFr

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
#define N 1010
#define mo 10007
using namespace std;
int c[N][N];
int tr[N][2],sz[N],f[N];
void link(int x,int y){
    tr[x][(tr[x][0]?1:0)]=y;
}
void dfs(int x)
{
    sz[x]=1;
    int l=tr[x][0],r=tr[x][1];
    if(!l && !r) {f[x]=1;return;}
    dfs(l);
    if(r) dfs(r);
    sz[x]+=sz[l]+sz[r];
    f[x]=c[sz[x]-1][sz[l]]*f[l]%mo;
    if(r) f[x]=f[x]*f[r]%mo;
}
int main()
{
    c[0][0]=1;
    fo(i,1,N-1)
    {
        c[i][0]=1;
        fo(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;
    } 
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        memset(tr,0,sizeof(tr));
        int n;
        scanf("%d",&n);
        fo(i,1,n)
        {
            int tot,ls=i;
            scanf("%d",&tot);
            fo(j,1,tot)
            {
                int x;
                scanf("%d",&x);
                link(ls,x),ls=x;
            }
        }
        dfs(1);
        printf("%d\n",f[1]);
    }
}
posted @ 2017-06-25 19:53  sadstone  阅读(62)  评论(0编辑  收藏  举报