[Ccodeforces 736C] Ostap and Tree - 樹形DP

給定一個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);
}

代碼魔改自:
一個神犇的博客

相關文章
相關標籤/搜索