LightOJ1382 The Queue(树形DP)

题目大概是说给一棵树的n个结点从1到n编号,要求每个结点的编号大于其父结点,问有多少种编号方式。

想了挺久的,感觉有点眉目,最后画了下样例YY出解法:

  • 首先求出以每个结点为根的子树大小,记为size[u],这个DFS一遍就可以求出来;
  • 接下来,dp[u]表示给以u为根的子树size[u]个编号有几种编号方案 
  • 然后考虑转移方程:
    • 比如一个结点u有3个儿子v1,v2,v3,那么u子树有size[u]个编号,编号最小的就属于u,剩下size[u]-1分配给u的三个子树,分配方式就有:

C(size[u]-1,size[v1])*C(size[u]-1-size[v1],size[v2])*C(size[u]-1-size[v1]-size[v2],size[v3])

    • 而v1、v2和v3子树对它们被分配的编号又分别有dp[v1]、dp[v2]和dp[v3]种编号方案,因此u子树的size[u]个编号总共的编号方式即dp[u]是:

dp[u] = dp[v1]*dp[v2]*dp[v3]*C(size[u]-1,size[v1])*C(size[u]-1-size[v1],size[v2])*C(size[u]-1-size[v1]-size[v2],size[v3])

于是就是这样用DP求解的。另外C(n,m)可以利用组合数的递推式C(n,m)=C(n-1,m)+C(n-1,m-1)预处理出来。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 struct Edge{
 6     int v,next;
 7 }edge[1111];
 8 int NE,head[1111];
 9 void addEdge(int u,int v){
10     edge[NE].v=v; edge[NE].next=head[u]; head[u]=NE++;
11 }
12 int n,size[1111];
13 long long C[1111][1111],d[1111];
14 int getSize(int u){
15     if(size[u]) return size[u];
16     int res=1;
17     for(int i=head[u]; i!=-1; i=edge[i].next){
18         int v=edge[i].v;
19         res+=getSize(v);
20     }
21     return size[u]=res;
22 }
23 long long dfs(int u){
24     if(d[u]) return d[u];
25     long long res=1;
26     int cnt=size[u]-1;
27     for(int i=head[u]; i!=-1; i=edge[i].next){
28         int v=edge[i].v;
29         res*=(dfs(v)*C[cnt][size[v]])%1000000007;
30         res%=1000000007;
31         cnt-=size[v];
32     }
33     return res;
34 }
35 int main(){
36     for(int i=0; i<1111; ++i) C[i][0]=1;
37     for(int i=1; i<1111; ++i){
38         for(int j=1; j<=i; ++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%1000000007;
39     }
40     int t,a,b;
41     scanf("%d",&t);
42     for(int cse=1; cse<=t; ++cse){
43         NE=0;
44         memset(head,-1,sizeof(head));
45         bool uroot[1111]={0};
46         scanf("%d",&n);
47         for(int i=1; i<n; ++i){
48             scanf("%d%d",&a,&b);
49             addEdge(a,b);
50             uroot[b]=1;
51         }
52         int root;
53         for(int i=1; i<=n; ++i){
54             if(!uroot[i]){
55                 root=i;
56                 break;
57             }
58         }
59         memset(size,0,sizeof(size));
60         getSize(root);
61         memset(d,0,sizeof(d));
62         printf("Case %d: %lld
",cse,dfs(root));
63     }
64     return 0;
65 }
原文地址:https://www.cnblogs.com/WABoss/p/5152160.html