2 条题解
-
0
树形DP树上背包首先,确定一下状态: 代表是以第 个节点为树根,其中放了 个特工,该节点被父亲 / 儿子观察,该节点放 / 不放特工。
其中,为确保状态不重,状态 的所有儿子均不可放特工。
确定状态之后,我们来看转移。
【转移】
先说 。
对于儿子 ,应该有前面放 个成立的方案数 节点放 个、被儿子看、自身不放的方案数,即 $dp[i][k][0][0]=\sum dp[i][k-j][0][0]\ \times \ dp[v][j][1][0]$。
二说 。
对于儿子 ,应该有前面放 个成立的方案数 节点放 个、被父亲 / 儿子看、自身不放的方案数,即 $dp[i][k][0][1]=\sum dp[i][k-j][0][1] \times (dp[v][j][1][0]+dp[v][j][0][0])$。
三说 。
对于儿子 ,应该有:
1.前面放 个成立的方案数 节点放 个、被儿子看、自身放 / 不放的方案数;
2.前面放 个,但 号节点仍未被观察的方案数 节点放 个、被儿子看、自身放的方案数;
即:$dp[i][k][1][0]=\sum (dp[i][k-j][1][0] \times (dp[v][j][1][0]+dp[v][j][1][1]) + dp[i][k-j][0][0] \times dp[v][j][1][1])$最后说 。
对于儿子 ,应该有:
1.前面放 个成立的方案数 节点放 个、被父亲 / 儿子看、自身放 / 不放的方案数;
2.前面放 个,但 号节点仍未被观察的方案数 节点放 个、被父亲 / 儿子看、自身放的方案数;
即:$dp[i][k][1][1]=\sum (dp[i][k-j][1][1] \times (dp[v][j][1][0]+dp[v][j][1][1]+dp[v][j][0][0]+dp[v][j][0][1]) + dp[i][k-j][0][1]* (dp[v][j][1][1]+dp[v][j][0][1])$最最后,注意 倒着枚举, 从 开始枚举,答案是 ( 为将无根树转成有根树时的树根),就可以了。
贴出代码:(因常数过大导致TLE,O2过了)
#include <bits/stdc++.h> using namespace std; int w;//题目中的k const int mod = 1e9 + 7; vector<int>e[100001];//建树 int size[100001];//子树大小 unsigned int dp[100001][101][2][2];//long long 会MLE void dfs1(int i, int fa) { size[i] = 1; for (int j = 0; j < e[i].size(); j++) if (e[i][j] != fa) { dfs1(e[i][j], i); size[i] += size[e[i][j]]; } } void dfs2(int i, int fa) { for (int j = 0; j < e[i].size(); j++) if (e[i][j] != fa) dfs2(e[i][j], i); dp[i][0][0][0] = dp[i][1][0][1] = 1; int qwq = 1; for (int j = 1; j <= e[i].size(); j++)//转移 if (e[i][j - 1] != fa) { qwq += size[e[i][j - 1]]; for (int k = min(w, qwq); k >= 0; k--) { unsigned long long int ans1 = 0, ans2 = 0, ans3 = 0, ans4 = 0; for (int x = 0; x <= min(k, size[e[i][j - 1]]); x++) { int v = e[i][j - 1]; if (k - x > qwq - size[v]) continue; (ans1 += ((unsigned long long )dp[i][k - x][0][0] * (dp[v][x][1][0]))) %= mod; (ans2 += ((unsigned long long )dp[i][k - x][0][1] * (dp[v][x][1][0] + dp[v][x][0][0]))) %= mod; (ans3 += ((unsigned long long )dp[i][k - x][1][0] * (dp[v][x][1][0] + dp[v][x][1][1]) + (unsigned long long )dp[i][k - x][0][0] * dp[v][x][1][1])) %= mod; (ans4 += ((unsigned long long )dp[i][k - x][1][1] * (dp[v][x][1][0] + dp[v][x][0][0] + dp[v][x][1][1] + dp[v][x][0][1]) + (unsigned long long )dp[i][k - x][0][1] * (dp[v][x][1][1] + dp[v][x][0][1]))) %= mod; } dp[i][k][0][0] = ans1; dp[i][k][0][1] = ans2; dp[i][k][1][0] = ans3; dp[i][k][1][1] = ans4; } } } int main() { int n; cin >> n >> w; for (int i = 1; i < n; i++) { int a, b; scanf("%d%d", &a, &b); e[a].push_back(b); e[b].push_back(a); } dfs1(1, 0); dfs2(1, 0); cout << (dp[1][w][1][0] + dp[1][w][1][1]) % mod; return 0; } -
0
// 树上背包 O(n*k) #include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=100005,mod=1000000007; int n,k,sz[N]; int f[N][105][2][2],g[105][2][2]; vector<int> e[N]; int add(LL x,LL y){ return (x+y)%mod; } void dfs(int u,int fa){ sz[u]=1; f[u][0][0][0]=f[u][1][1][0]=1; for(auto v:e[u]){ if(v==fa) continue; dfs(v,u); for(int i=0;i<=min(sz[u],k);++i){ g[i][0][0]=f[u][i][0][0], f[u][i][0][0]=0; g[i][0][1]=f[u][i][0][1], f[u][i][0][1]=0; g[i][1][0]=f[u][i][1][0], f[u][i][1][0]=0; g[i][1][1]=f[u][i][1][1], f[u][i][1][1]=0; } for(int i=0;i<=min(sz[u],k);++i){ for(int j=0;j<=min(sz[v],k-i);++j){ f[u][i+j][0][0]=add(f[u][i+j][0][0], g[i][0][0]*1ll*f[v][j][0][1]); f[u][i+j][1][0]=add(f[u][i+j][1][0], g[i][1][0]*(1ll*f[v][j][0][0]+f[v][j][0][1])); f[u][i+j][0][1]=add(f[u][i+j][0][1], g[i][0][0]*1ll*f[v][j][1][1] +g[i][0][1]*(1ll*f[v][j][1][1]+f[v][j][0][1])); f[u][i+j][1][1]=add(f[u][i+j][1][1], +g[i][1][0]*(1ll*f[v][j][1][0]+f[v][j][1][1]) +g[i][1][1]*(1ll*f[v][j][0][0]+f[v][j][0][1] +f[v][j][1][0]+f[v][j][1][1])); } } sz[u]+=sz[v]; } } int main(){ scanf("%d%d",&n,&k); for(int i=1,u,v;i<n;++i){ scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } dfs(1,0); printf("%d\n",(f[1][k][0][1]+f[1][k][1][1])%mod); }
- 1
信息
- ID
- 725
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 69
- 已通过
- 25
- 上传者
