1. 程式人生 > >【JZOJ B組】小W的動漫

【JZOJ B組】小W的動漫

Description

小W最近迷上了日本動漫,每天都有無數部動漫的更新等著他去看,所以他必須將所有的動漫排個順序,當然,雖然有無數部動漫,但除了1號動漫,每部動漫都有且僅有一部動漫是它的前傳(父親),也就是說,所有的動漫形成一個樹形結構。而動漫的順序必須滿足以下兩個限制: 1、一部動漫的所有後繼(子孫)都必須排在它的後面; 2、對於同一部動漫的續集(孩子),小W喜愛度高的須排在前面。 光排序小W還不爽,他想知道一共有多少種排序方案,並且輸出它mod 10007的答案。

Input

第一行表示T表示資料組數。接下來每組資料第一行n表示有多少部動漫等待排序,接下來n行每行第一個數tot表示這部動漫有多少部續集,接下來tot個數按照小W喜愛從大到小給出它的續集的編號。

Output

每組資料一行數ans,表示答案mod 10007的結果。

Sample Input

1 5 3 4 3 2 0 1 5 0 0

Sample Output

2

Data Constraint

30%的資料: n<=10 60%的資料: n<=100 100%的資料: n<=1000

思路

其實,可以發現,如果我們每一棵子樹是固定的 只要不改變字數內的相對位置,答案就是正確的。 所以我們考慮用插板法

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=3077,mod=10007;
int t,C[maxn][maxn],f[maxn],sum[maxn],list[maxn],cnt;
struct E
{
	int to,next;
}e[maxn];
void add(int u,int v)
{
	e[++cnt].to=v; e[cnt].next=list[u]; list[u]=cnt;
}
void pre()
{
	C[0][0]=1;
	for(int i=1; i<maxn; i++)
	{
		C[i][0]=1;
		for(int j=1; j<=i; j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
}
int c(int x,int y)
{
	return C[x][y];
} 
void dfs(int u)
{
	sum[u]=1; f[u]=1;
	for(int i=list[u]; i; i=e[i].next)
	{
		int v=e[i].to;
		dfs(v);
		f[u]=f[u]*f[v]%mod*c(sum[u]+sum[v]-2,sum[v]-1)%mod;
		sum[u]+=sum[v];
	}
}
int main()
{
	pre();
	scanf("%d",&t);
	while(t--)
	{
		int x,n,m;
		scanf("%d",&n); cnt=0;
		memset(e,0,sizeof(e)); memset(list,0,sizeof(list));
		for(int i=1; i<=n; i++)
		{
			scanf("%d",&m);
			for(int j=1; j<=m; j++) scanf("%d",&x),add(i,x);
		}
		dfs(1);
		printf("%d\n",f[1]);
	}
}