hdu 5378 Leader in Tree Land 2015多校联合训练赛 树形dp

来源:互联网 发布:佛山cnc编程招聘 编辑:程序博客网 时间:2024/03/28 18:20

Leader in Tree Land

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 169    Accepted Submission(s): 62


Problem Description
Tree land has n cities, connected by n1 roads. You can go to any city from any city. In other words, this land is a tree. The city numbered one is the root of this tree.

There are n ministers numbered from 1 to n. You will send them to n cities, one city with one minister. 

Since this is a rooted tree, each city is a root of a subtree and there are n subtrees. The leader of a subtree is the minister with maximal number in this subtree. As you can see, one minister can be the leader of several subtrees. 

One day all the leaders attend a meet, you find that there are exactly k ministers. You want to know how many ways to send n ministers to each city so that there are k ministers attend the meet.

Give your answer mod 1000000007.
 

Input
Multiple test cases. In the first line there is an integer T, indicating the number of test cases. For each test case, first line contains two numbers n,k. Next n1line describe the roads of tree land.

T=10,1n1000,1kn
 

Output
For each test case, output one line. The output format is Case #xansx is the case number,starting from 1.
 

Sample Input
23 21 21 310 82 13 24 15 36 17 38 79 710 6
 

Sample Output
Case #1: 4Case #2: 316512
 

Source
2015 Multi-University Training Contest 7

把1到n划分到n个结点的树种,子树的领导是这个子树中权值最大的点。

求n个结点的树种,领导为k个的情况数。


定义dp[u][i] 表示 子树u选择了i个领导的情况数。

假设处理了u的其他子树,  现在计算子树v

那么dp[u][i] = dp[u][j]*dp[v][i-j]

转移的时候只从有效状态进行转移,那么复杂度是O(n^2)的


对于每个跟,可以选这个点为领导有1种方案,不选它为领导有size[u]-1种方案。

因此得到两个有效状态。


对于一个子树v,规模为size[v]那么可以从没有使用的点中选着size[v]个点,那么

子树v有C(size[v],size[u]-1-用过的点数)中方案分点。所以处理完dp[v]需要乘以组合数


证明如下:归纳法

对于子树规模为K的子树,转移次数<=K*K

假设子树的复杂度 满足条件


对于u的子树的规模分别是k1,k2,k3 (这里假设为3个)

处理子树k1时,转移k1*2次即可,得到k1种有效状态(此时0状态其实没用了,算k1种即可),

处理k2时,转移的次数为k1*k2次,(背包的性质)

然后有(k1+k2)种有效状态了,再次转移是(k1+k2)*k3的复杂度

计算次数为 <= 2*k1 + k1*k2 + k1*k3+k2*k3 + k1*k1 + k2*k2 + k3*k3 (平方是因为子树的计算复杂度) 

<= (k1+k2+k3)*(k1+k2+k3) = k1*k1 + k2*k2 + k3*k3 + 2*k1*k2 + 2*k1*k3 + 2*k2*k3

显然k1*k2+k1*k3+k2*k3会比k1大。

证明完毕


#include<iostream>#include<cstdio>#include<cstring>#include<vector>using namespace std;#define maxn 1001#define ll long longll mod = 1e9+7;ll dp[maxn][maxn];vector<int>head[maxn];int size[maxn];ll res[maxn];ll C[maxn][maxn];void dfs(int u,int f){    size[u] = 1;    for(int i = 0;i < head[u].size(); i++){        int v = head[u][i];        if(v == f) continue;        dfs(v,u);        size[u] += size[v];    }    dp[u][1] = 1;    dp[u][0] = size[u] - 1;    int e = 1;    for(int i = 0;i < head[u].size(); i++){        int v = head[u][i];        if(v == f) continue;        for(int j = 0;j <= size[v] + e; j++)            res[j] = 0;        for(int j = 0;j <= size[v]; j++)                dp[v][j] = dp[v][j]*C[size[u]-e][size[v]]%mod;        for(int k = e;k >= 0; k--){            if(dp[u][k] == 0) continue;            ll x = dp[u][k];            for(int j = size[v];j >=0 ; j--){                res[k+j] = (res[k+j] +  dp[v][j]*x ) % mod;            }        }        e += size[v];        for(int j = 0;j <= e; j++)            dp[u][j] = res[j];    }}int main(){    memset(C,0,sizeof(C));    for(int i = 0;i < maxn; i++){        C[i][0] = 1;        for(int j = 1;j <= i; j++)            C[i][j] = (C[i-1][j] + C[i-1][j-1])%mod;    }    int t,n,k,tt=1;    scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&k);        memset(dp,0,sizeof(dp));        for(int i = 0;i <= n; i++)            head[i].clear();        int u,v;        for(int i = 1;i < n; i++){            scanf("%d%d",&u,&v);            head[u].push_back(v);            head[v].push_back(u);        }        memset(size,0,sizeof(size));        dfs(1,0);        printf("Case #%d: %I64d\n",tt++,dp[1][k]);    }    return 0;}


0 0