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
Case #1: 4 Case #2: 316512
求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; }