CF735E
题意:给一棵树,需要给树上的一些节点上色,要求任意节点在距离为k的范围以内至少有一个被染色的点,求方案数。
做法:显然的dp
树形DP。
用f[i][j]表示i的子树中离i最近黑点的距离为j,且距离超过j的点都被满足的方案数。转移时新建一个临时数组tmp保存转移后的f[x]。设y是x的子结点,枚举f[x][i]和f[y][j]
转移如下:
1.若i+j≤2k,则此时min(i,j+1)≤k,对于长度为i+j+1的链上的所有点都可以找到一边距离≤k,因此状态合并以后是合法状态,转移tmp[min(i,j+1)]+=f[x][i]×f[y][j];
2.若i+j>2k,则此时max(i,j+1)>k,链上肯定会存在一些点两边都够不到,转移tmp[max(i,j+1)]+=f[x][i]×f[y][j]
每个点都要存当前不合法的状态,因为i子树外的点可以建立黑点,使得当前不合法的之后合法
比如现在最远是k+x,如果i子树外某点p满足最远黑点为k-x,那么显然这个时候f[i][k+x]这个状态也合法了
初始状态f[x][0]=1,表示不考虑子树内的情况,选择自己的方案数为1;f[x][k+1]=1,表示自己本身不满足,但子结点都被满足的情况,主要是方便转移。
答案为∑i<=kf[root][i]
代码:
#include<bits/stdc++.h> #define N 2005 #define Mod 1000000007 using namespace std; int dp[N][N],f[N],head[N],n,k,x,y,kk; struct Tree{int nxt,to;}e[N]; inline void link(int x,int y){e[++kk].nxt=head[x];e[kk].to=y;head[x]=kk;} void dfs(int u,int fa){ dp[u][0]=dp[u][k+1]=1; for (int i=head[u];i;i=e[i].nxt){ int v=e[i].to; if (v==fa) continue; dfs(v,u); memset(f,0,sizeof(f)); for (int j=0;j<=2*k;j++){ for (int p=0;p<=2*k;p++){ if (j+p<=2*k){ f[min(p+1,j)]+=(1ll*dp[u][j]*dp[v][p])%Mod; f[min(p+1,j)]%=Mod; } else { f[max(p+1,j)]+=(1ll*dp[u][j]*dp[v][p])%Mod; f[max(p+1,j)]%=Mod; } } } for (int j=0;j<=2*k;j++) dp[u][j]=f[j]; } } int main(){ scanf("%d%d",&n,&k); for (int i=1;i<n;i++){ scanf("%d%d",&x,&y); link(x,y);link(y,x); } dfs(1,0);long long ans=0; for (int i=0;i<=k;i++) ans=1ll*(ans+dp[1][i])%Mod; printf("%lld\n",ans); return 0; }