[JZOJ5165] 小W的动漫
[JZOJ5165] 小W的动漫
(sort.cpp 1s 256M)
小WW最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲),也就是说,所有的动漫形成一个树形结构。而动漫的顺序必须满足以下两个限制:
①一部动漫的所有后继(子孙)都必须排在它的后面。
②对于同一部动漫的续集(孩子),小W喜爱度高的须排在前面。
光排序小WW还不爽,他想知道一共有多少种排序方案,并且输出它mod10007的答案。
Input
第一行表示T表示数据组数。
接下来每组数据第一行n表示有多少部动漫等待排序,
接下来n行每行第一个数tot表示这部动漫有多少部续集,
接下来tot个数按照小WW喜爱从大到小给出它的续集的编号。
n≤1000。
Output
每组数据一行数ans,表示答案mod10007的结果。
Sample Input
1
5
3 4 3 2
0
1 5
0
0
Sample Output
2
Solution
对于以X为根的子树, 其能组成的所有排序中, X及X的儿子节点的相对位置是唯一的, 而在这些点之下的点就可以变换排列.
设f(x)表示以x为根的子树能组成的合法序列个数;
在以fa(x)为根的子树里, x的子孙节点只能排布在x之后, 假设x后已经排列有t个节点, 那么x的子孙节点就有\(C_{sz(x)-1+t}^{sz(x)-1}\times f(x)\)种排列方式, 这也就是对f(fa(x))造成的贡献
最后答案就是f(1)
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
return x*f;
}
const int N=1e3+28,p=10007;
int n,sz[N],tr[N],tot[N],son[N][N];
int mul[N],di[N];
int Pow(int x,int y=p-2){
int re=1;
while(y){
if(y&1)re=re*x%p;
y>>=1;
x=x*x%p;
}
return re;
}
void Pre(int n=1000){
mul[1]=di[1]=1;
for(int i=2;i<=n;i++){
mul[i]=mul[i-1]*i%p;
di[i]=Pow(mul[i]);
}
}
int C(int n,int m){
if(m==0||n==m)return 1;
int re=mul[n]*di[m]%p;
re=re*di[n-m]%p;
return re;
}
void dfs(int x=1){
tr[x]=sz[x]=1;
int fk=0;
for(int i=tot[x];i>=1;i--){
dfs(son[x][i]);
sz[x]+=sz[son[x][i]];
tr[x]=tr[x]*C(fk+sz[son[x][i]]-1,fk)%p;
tr[x]=tr[x]*tr[son[x][i]]%p;
fk+=sz[son[x][i]];
}
}
signed main(){
//freopen("sort.in","r",stdin);
//freopen("sort.out","w",stdout);
Pre();
int t=read();
while(t--){
memset(tr,0,sizeof(tr));
n=read();
for(int i=1;i<=n;i++){
tot[i]=read();
for(int j=1;j<=tot[i];j++){
son[i][j]=read();
}
}
dfs();
printf("%lld\n",tr[1]);
}
return 0;
}
/*
1
5
3 4 3 2
0
1 5
0
0
*/