题意:
有一棵n个节点的树,现在要把其中一些节点染成黑色,要求染色后与任意一个点距离不超过k的所有点中至少存在一个黑色节点,n <= 100,0 <= k <= 20
solution:
定义状态f[i][j]:以i为根的子树中,能向上贡献j个单位/需要外界往内填补j个单位,方案数
如果是贡献,j为负数
转移的话,考虑不断合并子树,假如说当前处理x为根的子树
不妨把它的儿子按照输入顺序从左往右编号1~N
一开始到x的时候,初始状态f[x][-k] = f[x][1] = 1
然后不断把儿子的信息合并给x
做法是x与儿子枚举每一个可能的j值然后判断一下这样的状态转移后如何,添加到辅助数组里
最后把辅助数组的值copy给f[x],,
有大量调用的函数用常量引用可减少常数
O(n*k^2)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
typedef long long LL;
const LL mo = 1000000007;
int n,k,ans,f[111][50],g[50];
vector <int> v[111];
int F(const int &x) {return x + k;}
int Add(const LL &x,const LL &y) {return (x + y) % mo;}
int Mul(const LL &x,const LL &y) {return x*y%mo;}
void Dfs(int x,int fa)
{
f[x][F(-k)] = f[x][F(1)] = 1;
for (int i = 0; i < v[x].size(); i++)
{
int y = v[x][i];
if (y == fa) continue;
Dfs(y,x); memset(g,0,sizeof(g));
for (int a = -k; a <= k; a++)
{
if (!f[x][F(a)]) continue;
for (int b = -k; b <= k; b++)
{
if (!f[y][F(b)]) continue;
int t = Mul(f[x][F(a)],f[y][F(b)]);
if (a <= 0 && b <= 0)
{
int A = min(a,b + 1);
g[F(A)] = Add(g[F(A)],t);
}
else if (a > 0 && b > 0)
{
int A = max(a,b + 1);
g[F(A)] = Add(g[F(A)],t);
}
else if (a <= 0 && b > 0)
{
int A = (-a >= b)?a:b + 1;
g[F(A)] = Add(g[F(A)],t);
}
else if (a > 0 && b <= 0)
{
int A = (-b >= a)?b + 1:a;
g[F(A)] = Add(g[F(A)],t);
}
}
}
memcpy(f[x],g,sizeof(g));
}
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
cin >> n >> k;
for (int i = 1; i < n; i++)
{
int x,y; scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
Dfs(1,0);
for (int i = -k; i <= 0; i++)
ans = Add(ans,f[1][F(i)]);
cout << ans;
return 0;
}