zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    强制开启Android webview debug模式
    JavaScript DOM操作案例自定义属性的设置跟获取
    JavaScript innerText跟innerHTML的区别
    JavaScript DOM操作案例封装innerText跟textContent函数(浏览器兼容)
    JavaScript其他获取元素的方式
    JavaScript DOM操作案例根据类样式的名字获取元素
    JavaScript DOM操作案例根据name属性获取元素
    Java throws 使用
    理解 Android Build 系统
    理解Android编译命令
  • 原文地址:https://www.cnblogs.com/WABoss/p/5152160.html
Copyright © 2011-2022 走看看