Description
给出一棵以1为根的树,定义一棵树的一种序列,该序列满足:每个节点的所有儿子都要排在该节点后面,且每个节点的每个儿子,左边的要排在右边的前面(输入按顺序给出),求序列的可能方案数。模10007。
Solution
考虑转换模型,左边的儿子要比右边的儿子放在前面,那么让左边的儿子成为右边儿子的父亲,构出来一棵二叉树,那么现在求的就是满足父亲比儿子先遍历的遍历整棵树的方案数。
设
Fi
表示以i为根的子树的方案数,
Fl
表示左儿子的,
Fr
表示右儿子。
那么现在左边有
sizel
个点,走的方案为
Fl
,右边同理。
因为左右子树互不影响,所以两边可以交错着走,那么方案为:
Csizersizel+sizer⋅Fl⋅Fr
。
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]);
}
}