51nod 1353 樹 樹形DP經典題!

2022-03-30 06:04:32 字數 2172 閱讀 5087

切斷一棵樹的任意條邊,這棵樹會變成一棵森林。

現要求森林中每棵樹的節點個數不小於k,求有多少種切法。

資料範圍:\(n \le 2000\)。

//為什麼這道題做的人這麼少呢……感覺這道題超級經典,非常符合上周末模擬那種樹形dp的套路。會做這道題之後,可以想出許多類似的樹形dp。

首先狀態很好想:\(dp[u][i]\)表示「以u為根的子樹中,與u相連的聯通塊大小是i,剩下的聯通塊大小均大於k」的方案數。

下面的題解中,我們設要求的是\(dp[u][i]\),父親是u,兒子是v,sze[i]表示以i為根的子樹的大小。

為了方便起見,我們畫圖說明:

由於u有許多兒子,我們一次只處理乙個兒子,處理的時候,就相當於將一堆新的節點(子樹v)新增到原有的圖。

現在與u相連的聯通塊大小為i,與j相連的大小為j。顯然對於(u, v)之間的這條邊,要麼切斷,要麼不切斷。上圖描述的是切斷的情況(這種情況要求j >= k)。顯然新的\(dp[u][i] += dp[u][i] * dp[v][j]\)。(注意,等式右邊的\(dp[u][i]\)是在聯通塊中加入子樹v之前的答案)。

另一種情況是不切斷這條邊。如下圖:

由於沒切斷,加入新子樹之後,與u相連的聯通塊的大小由i變成了i + j。

那麼顯然:\(dp[u][i + j] += dp[u][i] * dp[v][j]\)。(等式右邊的dp[u][i]仍是加入子樹之前的)。

現在我們連狀態轉移方程都想好了,寫**是不是就很簡單了呢?可能……並不是……(至少對於我來說)

十分需要注意的地方是\(dp[u][i]\)意義的變化。我們必須保證等式右邊的\(dp[u][i]\)永遠是加入子樹之前的答案。例如,對於切斷(u, v)這條邊的情況,如果我們在u、v沒有改變的情況下,直接這樣迴圈:

for r i : 1 -> sze[u]

for j : 1 -> sze[v]

dp[u][i] += dp[u][i] * dp[v][j]

顯然是不行的。因為等式右側的\(dp[u][i]\)一經修改就不是「加入子樹v之前的」了。

一種合理的解決方案是用\(dp[v][0]\)表示與v相連的聯通塊大小大於等於k的所有方案數之和,這樣對於切斷(u, v)的情況,直接\(dp[u][i] = dp[u][i] * dp[v][0]\)即可,前提是在這一步之前\(dp[u][i]\)沒有更新過。所以我們接下來讓更小的i中的「不切斷的情況」來更新這裡的\(dp[u][i]\)。這裡直接\(dp[u][i + j] += dp[u][i] * dp[v][j]\)是沒有問題的。

啊對了!這個**看起來是\(o(n^3)\)的啊!

……但其實由於每次兩層迴圈i、j分別列舉了子樹u(的已知部分)和子樹v的點,時間複雜度增加了sze[u] * sze[v],相當於列舉了每一對點,那麼總計每一對點只被列舉了一次。總計\(o(n^2)\)。

**:

#include #include #include using namespace std;

typedef unsigned long long ll;

#define inf 0x3f3f3f3f

#define enter putchar('\n')

#define space putchar(' ')

template bool read(t &x)

template void write(t x)

const int n = 2005, p = 1e9+7;

int n, k, sze[n];

int ecnt, adj[n], nxt[2*n], go[2*n];

ll dp[n][n], ans;

void add(int u, int v)

void dfs(int u, int pre)

sze[u] += sze[v];

}for(int i = k; i <= sze[u]; i++)

dp[u][0] = (dp[u][0] + dp[u][i]) % p;

}int main()

51Nod 1378 樹形DP 貪心

題目鏈結 題意 給定一棵樹和乙個整數k kk,可以在樹的任意位置放置乙個人,這個人可以監管距離其所在節點不超過k kk的所有節點,問最少放置多少個人可以監管整棵樹的所有節點。思路 此題的解題思路還挺具有啟發意義。對於樹的相關問題,我們可以考慮先轉換成線性問題來做,如果是線性的話,很顯然最優解應該是從...

51nod 1405 樹的距離之和 樹形dp

給定一棵無根樹,假設它有n個節點,節點編號從1到n,求任意兩點之間的距離 最短路徑 之和。input 第一行包含乙個正整數n n 100000 表示節點個數。後面 n 1 行,每行兩個整數表示樹的邊。output 每行乙個整數,第i i 1,2,n 行表示所有節點到第i個點的距離之和。input示例...

51nod 1500 蘋果曼和樹(樹形dp)

題目 思路 分析狀態間的關係 dp u 1 0 代表與u相連的聯通塊是否具有黑點的方案數 注意有刪邊 dp u 1 當u相連的聯通塊具有黑點時,兒子結點v具有黑點與否都可以 當u相連的聯通塊不具有黑點時,兒子結點必須具有黑點 dp u 0 u相連的聯通塊肯定不具有黑點,兒子結點具有黑點與否都可以 i...