2 9 2 1 3 1 4 3 5 3 6 2 7 4 8 7 9 3 8 2 1 3 1 4 3 5 1 6 4 7 5 8 4
Case #1: 32 Case #2: 16
一棵树N个结点,编号1-N,问有多少种编号方法满足:每个节点的子结点编号是连续的,任意一棵子树的编号是连续的。
以某个结点为根的子树,假设根节点编号已经确定,这个根节点最多只能有两个非叶子儿子,因为非叶子儿子编号取值必须是整个编号范围的两端,所以如果有结点的非叶子儿子大于两个的话肯定是不行的。刚开始做的时候dfs还加上了编号范围,但这是没必要的,因为只要是一段连续的数,种数都是相同的。用dfs(u)表示以u为根的树有多少种满足的编号情况(u的编号已经确定,并且剩下结点编号是连续的一段)。非叶子的儿子只能取两端的编号,接着dfs,叶子编号是可以随意排的,所以再乘上叶子个数的阶乘。注意只有一个结点的情况,特判一下。
#include<cstring> #include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; typedef long long LL; const int MAXN=100010; const LL MOD= 1000000007; int T,N; LL fac[MAXN]; int cnt[MAXN],num[MAXN]; vector<int> g[MAXN]; int dfs1(int u,int fa){ int len=g[u].size(); num[u]=1; cnt[u]=0; for(int i=0;i<len;i++){ int v=g[u][i]; if(v!=fa){ int n=dfs1(v,u); num[u]+=n; if(n>1) cnt[u]++; } } return num[u]; } LL dfs2(int u,int fa){ int len=g[u].size(); if(cnt[u]>2) return 0; int lef=0,v1=-1,v2=-1; for(int i=0;i<len;i++){ int v=g[u][i]; if(v!=fa){ if(num[v]<=1) lef++; else{ if(v1==-1) v1=v; else v2=v; } } } LL ret=0; LL m=fac[lef]; if(v1!=-1){ if(v2!=-1) ret=(ret+m*dfs2(v1,u)%MOD*dfs2(v2,u)%MOD*2%MOD)%MOD; else ret=(ret+m*dfs2(v1,u)%MOD*2%MOD)%MOD; } else ret=m; return ret; } int main(){ freopen("in.txt","r",stdin); scanf("%d",&T); int cas=0; fac[0]=1; for(int i=1;i<100010;i++) fac[i]=fac[i-1]*i%MOD; while(T--){ scanf("%d",&N); int u,v; for(int i=1;i<=N;i++) g[i].clear(); for(int i=0;i<N-1;i++){ scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } memset(cnt,0,sizeof(cnt)); dfs1(1,-1); printf("Case #%d: ",++cas); if(N==1) printf("1\n"); else printf("%I64d\n",2*dfs2(1,-1)%MOD); } return 0; }