题目大意:
一个$n(nle100)$个点的树,将一些点染成黑点,求满足每个点到最近黑点的距离$le k(klemin(20,n-1))$的方案数。
思路:
树形DP。
用$f[i][j]$表示$i$的子树中离$i$最近黑点的距离为$j$,且距离超过$j$的点都被满足的方案数。转移时新建一个临时数组$tmp$保存转移后的$f[x]$。设$y$是$x$的子结点,枚举$f[x][i]$和$f[y][j]$,转移如下:
1.若$i+jle2k$,则此时$min(i,j+1)le k$,对于长度为$i+j+1$的链上的所有点都可以找到一边距离$le k$,因此状态合并以后是合法状态,转移$tmp[min(i,j+1)]+=f[x][i] imes f[y][j]$;
2.若$i+j>2k$,则此时$max(i,j+1)>k$,链上肯定会存在一些点两边都够不到,转移$tmp[max(i,j+1)]+=f[x][i] imes f[y][j]$。
初始状态$f[x][0]=1$,表示不考虑子树内的情况,选择自己的方案数为$1$;$f[x][k+1]=1$,表示自己本身不满足,但子结点都被满足的情况,主要是方便转移。
答案为$sum_{i<=k}f[root][i]$。
时间复杂度$O(nk^2)$。
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 #include<forward_list> 5 typedef long long int64; 6 inline int getint() { 7 register char ch; 8 while(!isdigit(ch=getchar())); 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int N=101,K=41,mod=1e9+7; 14 int k,f[N][K],tmp[K]; 15 std::forward_list<int> e[N]; 16 inline void add_edge(const int &u,const int &v) { 17 e[u].push_front(v); 18 e[v].push_front(u); 19 } 20 void dfs(const int &x,const int &par) { 21 f[x][0]=f[x][k+1]=1; 22 for(int &y:e[x]) { 23 if(y==par) continue; 24 dfs(y,x); 25 std::fill(&tmp[0],&tmp[k*2]+1,0); 26 for(register int i=0;i<=k*2;i++) { 27 for(register int j=0;j<=k*2;j++) { 28 (tmp[i+j<=k*2?std::min(i,j+1):std::max(i,j+1)]+=(int64)f[x][i]*f[y][j]%mod)%=mod; 29 } 30 } 31 std::copy(&tmp[0],&tmp[k*2]+1,f[x]); 32 } 33 } 34 int main() { 35 const int n=getint();k=getint(); 36 for(register int i=1;i<n;i++) { 37 add_edge(getint(),getint()); 38 } 39 dfs(1,0); 40 int ans=0; 41 for(register int i=0;i<=k;i++) { 42 (ans+=f[1][i])%=mod; 43 } 44 printf("%d ",ans); 45 return 0; 46 }