标签:
题意:给出一棵树,用[1,n]里的n个不同的数去给结点标号,要求任意结点的儿子标号都是连续的,任意子树里的标号也都是连续的。求这样标号的方法有多少种。
做法:很显然,若一个结点有大于两个的儿子必然无解。
若当前有x个连续的数可以用来分配,那么这个结点要么取最小的那个数,要么取最大的那个数,
当该结点刚好有两个不为叶子的儿子,然后剩下的那x-1个数要么把前面的部分给左边的儿子要么给右边的儿子,后面的部分一样,这就有2种可能。
当刚好有一个不为叶子的儿子,那么剩下的那x-1个数要么把前面的部分给儿子,要么把后面的部分给儿子,这就有2种可能。
若没有上述的儿子,则全是叶子,则就是(x-1)!可能,上述两种情况考虑儿子为叶子的时候同理。
接下来就是很裸的树形dp了。
#pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<cstdio> #include<cstring> #include<vector> using namespace std; typedef long long ll; const ll mod=1000000007; struct Edge { int to,next; }edge[200010]; int head[100010],tail; void add(int from,int to) { edge[tail].to=to; edge[tail].next=head[from]; head[from]=tail++; } bool vis[100010]; int num[100010]; void seek(int from) { vis[from]=1; num[from]=1; for(int i=head[from];i!=-1;i=edge[i].next) { int to=edge[i].to; if(vis[to]) continue; seek(to); num[from]+=num[to]; } } ll fac[100010]; ll dfs(int from) { vis[from]=1; int sum1=0,sum2=0; vector<int>bx; for(int i=head[from];i!=-1;i=edge[i].next) { int to=edge[i].to; if(vis[to]) continue; if(num[to]==1) sum1++; else { sum2++; bx.push_back(to); } } if(sum2>2) { return 0; } if(sum2==0) return fac[sum1]; ll sum=1; for(int i=0;i<sum2;i++) { ll t=dfs(bx[i]); if(t==0) return 0; sum=(sum*t)%mod; } return fac[sum1]*sum*2%mod; } int main() { fac[0]=1; for(int i=1;i<=100000;i++) fac[i]=fac[i-1]*i%mod; int T; scanf("%d",&T); for(int cs=1;cs<=T;cs++) { int n; scanf("%d",&n); tail=0; memset(head,-1,sizeof(head)); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } if(n==1) { printf("Case #%d: 1\n",cs); continue; } memset(vis,0,sizeof(vis)); seek(1); memset(vis,0,sizeof(vis)); printf("Case #%d: %d\n",cs,int(2*dfs(1)%mod)); } return 0; }
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
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/stl112514/article/details/47447451