【Codeforces 735E】Ostap and Tree
树形 DP。
题目大意:
给定一棵树,要给一些结点染色,并且需要满足距离每个点最近的染色点离它的距离不超过 $k$。求方案数。
题解:
动态规划。
令 $f_{i,j}$ 表示以 $i$ 为根的子树中,有一个最近的黑点距离根为 $j$ 且距离 $j$ 以上的点都已经有对应的染色点。
也就是说,如果 $i$ 子树中全都有距离为 $k$ 以内的染色点,则 $j$ 表示其子树中最浅的一个染色点。反正 $j$ 就表示子树中最深的且到根路径上没有别的染色点的染色点。
转移的时候枚举 $j,t$,考察 $j+t+1$ 和 $2k+1$ 的大小关系进行转移即可。具体见代码的 dfs 部分。
时间复杂度 $O(nk^2)$。
Code:
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=114,md=1e9+7;
int n,k,dp[N][42],g[42];
vector<int>G[N];
inline void add(int&a,LL b){a=(a+b)%md;}
int dfs(int now,int pre){
bool flg=0;
int dep=1;
int*f=dp[now];
for(int to:G[now])if(to!=pre){
dep=max(dfs(to,now)+1,dep);
if(!flg){
flg=1;
for(int i=0;i<=2*k;++i)f[i+1]=dp[to][i];
}else{
memset(g,0,sizeof g);
for(int i=0;i<=2*k+1;++i)
for(int j=0;j<=2*k+1;++j)
if(i+j+1<=2*k+1)
add(g[min(i,j+1)],f[i]*(LL)dp[to][j]);
else
add(g[max(i,j+1)],f[i]*(LL)dp[to][j]);
memcpy(f,g,sizeof g);
}
}
if(dep==1)
f[0]=f[k+1]=1;
else{
for(int i=1;i<=2*k+1;++i)add(f[0],f[i]);
}
return dep;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<n;++i){
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v),G[v].push_back(u);
}
dfs(1,0);
int ans=0;
for(int i=0;i<=k;++i)add(ans,dp[1][i]);
printf("%d\n",ans);
return 0;
}