[CF736C](Ostap and Tree)

  • 题意

给定一个n个点的树,把其中一些点涂成黑色,使得对于每个点,它离与它最近的黑点的距离不超过K,求方案总数.

  • solution

树形dp

我们设\(dp[i][r]\)

状态:当前点为i,离i最近的黑点与i距离为r.

dp[i][r]的意义: 在i的子树内 达成该状态的方案数

更新方式当然是递归,让儿子更新父亲,同时把儿子的答案统计到父亲上,这样就把答案汇总到根上了.

一开始我想的是r取值为0~k,结果是不行的,因为离i最近的黑点不仅可能在i的子树内,还可能在i的子树外

但我们(在更新dp[i][r]时)只考虑了黑点在i的子树内的情况.

所以r的取值应该在0~2*k间,这样 即使不合法也要记下来(重要!!) ,因为它子树外的黑点仍然可以使它变成合法的.

然后我们考虑如何转移

设v为u的儿子枚举v和u的dp值第二维

对于dp[u][i]和dp[v][r],我们这样转移

如果\(i+(r+1)<=2\times k+1\)那么\(tmp[min(i,r+1)]+=dp[u][i]\)(tmp为临时数组,转移后赋值到dp[u][i]上,代表u子树内的方案数统计)

这个方程代表的意思是该方案可行时的转移,然后min(i,r+1)的意思是

因为这个方案自身的问题已经解决,还(有余力)可以用来更新其他状态,简单的说就是min(i,r+1)代表的那个黑点还可以发挥作用

如果\(i+(r+1)>2\times k+1\)那么\(tmp[max(i,r+1)]+=dp[u][i]\)

这个方程代表的意思是该方案不可行时的转移,然后max(i,r+1)的意思是

因为这个方案自身的问题还没解决,需要别的黑点来更新它,

而因为要更新(使这个方案变得可行)就要照顾到所有点,而其中离黑点最远的点在哪里呢,就是在max(i,r+1)对应的黑点到u的链上,所以我们要把这条链更新,就应该记max(i,r+1)

r+1是因为从u到v有1的距离

初始化:dp[u][0]=1(u为黑点)dp[u][k+1]=1(u不为黑点,需要一个距离小于等于k的点来更新它)

  • code

#include<bits/stdc++.h>
#define N 205
#define int long long//懒
using namespace std;
const int mod=1e9+7;
vector<int> G[N];
int dp[N][N];
int plk[N];
int n,k;
void dfs(int x,int fa){
  dp[x][0]=dp[x][k+1]=1;
  for(int i=0;i<G[x].size();i++){
    int to=G[x][i];
    if(to==fa)continue;
    dfs(to,x);
    for(int i=0;i<=2*k+1;i++)plk[i]=0;
    for(int r=0;r<=2*k+1;r++)
    for(int p=0;p<=2*k;p++){
      if(r+p<=2*k)(plk[min(r,p+1)]+=1ll*dp[x][r]*dp[to][p])%=mod;
      else (plk[max(r,p+1)]+=1ll*dp[x][r]*dp[to][p])%=mod;
      }
    for(int r=0;r<=2*k+1;r++)dp[x][r]=plk[r];
    }
  }
signed main(){
  scanf("%lld%lld",&n,&k);
  for(int i=1;i<n;i++){
    int a,b;
    scanf("%lld%lld",&a,&b);
    G[a].push_back(b);
    G[b].push_back(a);
    }
  dfs(1,0);
  int ans=0;
  for(int i=0;i<=k;i++)(ans+=dp[1][i])%=mod;
  cout<<ans;
}
posted @ 2019-03-07 11:28  stepsys  阅读(353)  评论(0编辑  收藏  举报

*/