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

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  n1 line describe the roads of tree land.

T=10,1n1000,1kn
 

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

Sample Input
  
  
2 3 2 1 2 1 3 10 8 2 1 3 2 4 1 5 3 6 1 7 3 8 7 9 7 10 6
 

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

Source

把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 long
ll 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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值