CF2231E
Gaochenxi103_QAQ · · 题解
伟大宋队的树上背包做法。
做法
想到树形 DP 是容易的,难点在于状态的定义和转移的细节。题目要求大小为
每个方案在其连通块深度最小点统计。答案为
初始化每个点只选自己且钦定的方案,
题目要求没有多余的选择,如果子树
如果钦定,那么枚举当前状态总选择点数
满足
注意这是一个树上背包,复杂度是
如果不钦定,那么相当于是在
注意这个计算要在前一个转移后,以免后面转移要用的 DP 值,包含了
总时间复杂度就是树上背包的
代码
::::success[Accepted code]
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2020;
int n,d,ans;
int siz[N],dp[N][N][4];
vector<int>G[N];
void dfs(int u,int fa)
{
for(auto v:G[u])
{
if(v==fa)continue;
dfs(v,u);
}
dp[u][1][1]=1;
siz[u]=1;
for(auto v:G[u])
{
if(v==fa)continue;
for(int i=siz[u]+siz[v];i>=2;i--)
{
for(int j=min(siz[v],i-1);j>=1;j--)
{
for(int x=2;x<=3;x++)
{
for(int y=1;y<x;y++)
{
dp[u][i][x]=dp[u][i][x]+dp[u][i-j][y]*dp[v][j][x-y];
}
}
}
}
for(int j=1;j<=siz[v];j++)
{
for(int k=1;k<=2;k++)
{
dp[u][j+1][k]=dp[u][j+1][k]+dp[v][j][k];
}
}
siz[u]+=siz[v];
}
}
void clear()
{
for(int i=1;i<=n;i++)G[i].clear();
for(int i=1;i<=n;i++)siz[i]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=3;k++)
{
dp[i][j][k]=0;
}
}
}
}
void solve()
{
cin>>n>>d;
for(int i=1;i<n;i++)
{
int u,v;cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
ans=0;
for(int i=1;i<=n;i++)ans+=dp[i][d][3];
cout<<ans<<"\n";
clear();
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;cin>>T;
while(T--)solve();
return 0;
}
::::