給定一個n個點的樹,把其中一些點塗成黑色,使得對於每一個點,其最近的黑點的距離不超過K.
樹形DP.編程
設置狀態f[i][j]:數組
當j <= K時:spa
合法狀態,表示i的子樹中到根的最近黑點距離爲j的方案數..net
當 K < j <= 2K時:設計
不合法狀態,表示i的子樹中,須要在上面補充黑點,且這個黑點到i的距離應該至多爲(2K - j + 1).code
對於這個狀態的另一種理解方式是:i的子樹中,距離i最近的黑點距離超過K,距離i最遠的黑點距離爲j,方案數.blog
設計這樣狀態的動機在於:get
若是僅僅有j <= K的狀態,那麼可能僅僅知足了某些點的要求,而沒有知足全部點, 或者, 僅僅考慮子樹中對於點的影響, 而不考慮祖先, 換言之, 狀態具備後效性.input
這樣的狀態設置是精妙的, 實際編程中大大減小了編程複雜度.博客
轉移的時候, 依次加入點x的每個兒子樹, 而後枚舉已經造成的樹找到過的j, 和對於這個子樹的k:
若是j + (k + 1) <= 2K + 1, 那麼說明把這個方案加到樹裏能夠直接構成一個合法方案, 那麼直接統計.
不然,說明須要上面的黑點,那麼放進不合法狀態中.
實現的時候開一個臨時數組便可.
具體方程和細節見代碼.
初始化的時候:
f[x][0] = f[x][k+1] = 1;
剛開始的時候只有x一個節點, 而後這個節點選與不選兩種方案.
答案統計f[1][i](i <= K)便可.
#include <cstdio> #include <algorithm> #define ll long long #define For(i,j,k) for(ll i=j;i<=k;i++) using namespace std; ll mo=1e9+7; ll poi[10001],F[10001],nxt[10001], dep[1001],f[1001][1001],tmp[1001],ans,n,k,x,y,cnt; bool vis[1001]; inline void add(ll x,ll y){poi[++cnt]=y;nxt[cnt]=F[x];F[x]=cnt;} inline void dfs(ll x) { vis[x] = 1; f[x][0] = 1; f[x][k+1] = 1; for(ll i=F[x];i;i=nxt[i]) { ll ne = poi[i]; if(vis[ne]) continue; dep[ne] = dep[x] + 1; dfs(ne); For(j,0,2*k) tmp[j]=0; For(j,0,2*k) For(t,0,2*k+1) { if(j+t<=2*k) tmp[min(j,t + 1)] += f[x][j] * f[ne][t], tmp[min(j,t+1)] %= mo; else tmp[max(j,t + 1)] += f[x][j] * f[ne][t], tmp[max(j,t+1)] %= mo; } For(j, 0,2*k) f[x][j]=tmp[j]; } } int main() { #ifdef orz freopen("input", "r", stdin); #endif scanf("%lld %lld", &n, &k); if(k == 0) return puts("1") & 0; For(i,1,n-1) { scanf("%lld %lld", &x, &y); add(x, y); add(y, x); } dfs(1); For(i,0,k) ans+=f[1][i],ans%=mo; printf("%lld", ans); }
代碼魔改自:
一個神犇的博客