[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
*/

posted @ 2019-09-08 20:52  The_KOG  阅读(406)  评论(0编辑  收藏  举报