**Chủ đề là DP trên cây nên các bạn đừng làm tham nhé 😑** Các bài quy ước $1$ là gốc cây. ## Bài 1: [Tô màu cây](https://marisaoj.com/problem/230) - Trạng thái DP: $f(u, i)$ là số lượng đỉnh đen lớn nhất khi tô hết cây con gốc $u$ và $u$ có màu $i$. Quy ước $0$ là màu trắng còn $1$ là màu đen. - Cập nhật: + $f(u,0)=\sum{max(f(v,1), f(v,0))}$ với $v$ là con trực tiếp của $u$. Do $u$ màu trắng nên các con $v$ có thể màu trắng hoặc đen. + $f(u,1)=\sum{f(v,0)}$, do $u$ đen nên các con $v$ chỉ có thể là màu trắng. - Đáp án là $max(f(1,0/1))$. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5; vector<int> adj[MAXN+5]; int dp[MAXN+5][2]; void dfs(int u, int p) { dp[u][0] = 0; dp[u][1] = 1; for (int v : adj[u]) { if (v == p) continue; dfs(v, u); dp[u][0] += max(dp[v][0], dp[v][1]); dp[u][1] += dp[v][0]; } } int main() { int n; cin >> n; for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } dfs(1, 0); cout << max(dp[1][0], dp[1][1]) << endl; return 0; } ``` ::: ## Bài 2: [Tô màu cây 2](https://marisaoj.com/problem/232) - Trạng thái DP: $f(u,i)$ là số lượng cách tô màu khi tô hết cây con gốc $u$ và $u$ có màu $i$. $i$ từ $0$ tói $2$ - Cập nhật: + $f(u,0)=\prod{(f(v,1) + f(v,2))}$, do với mỗi đỉnh $v$, ta có thể tô hoặc là màu $1$ hoặc là màu $2$. + Tương tự với các màu còn lại. - Đáp án $f(1,1)+f(1,2)+f(1,3)$. Code: :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define int long long const int MOD = 1e9 + 7; const int MAXN = 1e5; vector<int> adj[MAXN + 1]; int dp[MAXN + 1][3]; void dfs(int u, int p) { dp[u][0] = dp[u][1] = dp[u][2] = 1; for (int v : adj[u]) { if (v == p) continue; dfs(v, u); dp[u][0] = (dp[u][0] * (dp[v][1] + dp[v][2])) % MOD; dp[u][1] = (dp[u][1] * (dp[v][0] + dp[v][2])) % MOD; dp[u][2] = (dp[u][2] * (dp[v][0] + dp[v][1])) % MOD; } } signed main() { int n; cin >> n; for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } dfs(1, 0); int ans = (dp[1][0] + dp[1][1] + dp[1][2]) % MOD; cout << ans << endl; return 0; } ``` ::: ## Bài 3: [Đường đi có tổng lớn nhất](https://marisaoj.com/problem/229) - Trước tiên, đương đi đơn có 2 dạng như sau, một số bạn chỉ tính dạng 1 thì sai: ![](https://hackmd.io/_uploads/ByivVhtun.png) - Nhưng trước tiên, cứ tính dạng 1 trước với trạng thái $f(u)$ là giá trị đường đi có tổng lớn nhất bắt đầu ở $u$. - Cập nhật: + $f(u)=max(0,max(f(v))+a_u$. Có thể nối $u$ với một đường đi bắt đầu ở $v$ nào đó, hoặc không bắt đầu một đường đi mới chỉ có mỗi $u$. - Để xử lí trường hợp đường đi thứ 2, với mọi $u$, chọn ra $v$ và $v'$ là con trực tiếp của $u$ sao cho $f(v)$ và $f(v')$ lớn nhất, nối đường đi từ $v$ lên $u$ xuống $v'$. - Đáp án là $max(f(u))$. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; const int MAXN = 1e5; #define int long long vector<int> adj[MAXN+5]; int A[MAXN+5]; int dp[MAXN+5]; int ans = LLONG_MIN; void dfs(int u, int p) { dp[u] = A[u]; vector<int> f; for (int v : adj[u]) { if (v == p) continue; dfs(v, u); dp[u] = max(dp[u], A[u] + dp[v]); f.push_back(dp[v]); } sort(f.begin(), f.end(), greater<int>()); if(f.size() > 1) ans = max(ans, A[u] + f[0]+ f[1]); } signed main() { int n; cin >> n; for (int i = 1; i <= n; i++) { cin >> A[i]; } for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } dfs(1, 0); for (int i = 1; i <= n; i++) { ans = max(ans, dp[i]); } cout << ans << endl; return 0; } ``` ::: ## Bài 4: [Đường đi độ dài k](https://marisaoj.com/problem/231) - Tương tự bài trên, trạng thái DP để tính đường đi loại 1: $f(u,i)$, số lượng đường đi bắt đầu ở $u$ và có độ dài $i$. - Cập nhật: + $f(u,i)=\sum{f(v,i-1)}$. - Với đường đi loại 2, ví dụ ta nối một đường đi từ $v$ lên $u$, từ $u$ sẽ phải xuống một đỉnh $v'$ khác. Nếu nối một đường đi độ dài $p$ từ $v$ lên $u$, thì $v'$ phải có độ dài $k - p - 1$. Nghĩa là tính tổng: $f(v,p) \times f(v',k-p-1)$ với $v'$ khác $v$. Code :::spoiler ```c++ #include <bits/stdc++.h> using namespace std; #define int long long const int MAXN = 1e5; const int MAXK = 100; vector<int> adj[MAXN+5]; int dp[MAXN+5][MAXK+5]; int n, k; int ans = 0; void dfs(int u, int p) { dp[u][0] = 1; for (int v : adj[u]) { if (v == p) continue; dfs(v, u); for (int i = 1; i <= k; i++) { dp[u][i] += dp[v][i-1]; } } // for(int i = 1; i <= k; i++) cout << dp[u][i] << ' '; // cout << endl; int t = 0; for (int v : adj[u]) { if(v == p) continue; for(int i = 1; i < k; i++){ t += dp[v][i - 1] * (dp[u][k - i] - dp[v][k - i - 1]); } } ans += t / 2; //cout << ans << endl; } signed main() { cin >> n >> k; for (int i = 1; i < n; i++) { int u, v; cin >> u >> v; adj[u].push_back(v); adj[v].push_back(u); } dfs(3, 0); // cout << ans << endl; for (int i = 1; i <= n; i++) { ans += dp[i][k]; } cout << ans << endl; return 0; } ``` :::