This repository has been archived on 2022-06-22. You can view files and clone it, but cannot push or open issues or pull requests.
usaco-guide/content/4_Gold/DP_Trees.mdx
2020-07-18 21:36:48 -04:00

185 lines
4.4 KiB
Text

---
id: dp-trees
title: "Dynamic Programming on Trees"
author: Michael Cao
prerequisites:
- dfs
- intro-dp
description: "Using subtrees as subproblems."
frequency: 2
---
import { Problem } from "../models";
export const metadata = {
problems: {
sample: [
new Problem("CSES", "Tree Matching", "1130", "Easy", false, ["DP"], ""),
],
allRoots: [
new Problem("AC", "Subtree", "https://atcoder.jp/contests/dp/tasks/dp_v", "Normal", false, [], ""),
],
usaco: [
new Problem("AC", "Independent Set", "https://atcoder.jp/contests/dp/tasks/dp_p", "Easy", false, [], ""),
new Problem("Gold", "Barn Painting", "766", "Easy", false, ["DP"], "similar to independent set on tree"),
new Problem("ojuz", "COCI - Dzumbus", "COCI19_dzumbus", "Hard", false, [], ""),
new Problem("Gold", "Directory Traversal", "814", "Normal", false, [], ""),
new Problem("Gold", "Delegation", "1019", "Hard", false, ["Greedy"], ""),
new Problem("Plat", "Delegation", "1020", "Hard", false, ["DP", "Binary Search"], ""),
new Problem("CF", "Ostap & Tree", "problemset/problem/735/E", "Hard", false, ["DP"], "$O(NK)$ I think"),
new Problem("CSES", "Creating Offices", "1752", "Hard", false, ["Greedy"], "equivalent to BOI - Cat in a Tree"),
],
}
}
<Problems problems={metadata.problems.sample} />
## Tutorial
<Resources>
<Resource source="CF" title="DP on Trees" url="blog/entry/20935"> </Resource>
<Resource source="Philippines" title="DP on Trees and DAGs" url="https://noi.ph/training/weekly/week5.pdf">lol this code format is terrible </Resource>
</Resources>
## Sample Solution
## Solving for All Roots
<Problems problems={metadata.problems.allRoots} />
(dfs twice)
<Spoiler title="Solution">
<LanguageSection>
<CPPSection>
```cpp
template<int SZ> struct SubtreeDP {
int par[SZ]; vi adj[SZ];
void ae(int a, int b) { adj[a].pb(b), adj[b].pb(a); }
struct T {
mi v = 1;
T& operator+=(const T& b) { v *= b.v; return *this; }
void tran() { ++v; }
};
T up[SZ], down[SZ];
void dfs(int x) {
trav(t,adj[x]) if (t != par[x]) {
par[t] = x; dfs(t);
down[x] += down[t];
}
down[x].tran();
}
void dfs2(int x) {
{
T pre = up[x];
F0R(i,sz(adj[x])) {
int c = adj[x][i]; if (c == par[x]) continue;
up[c] += pre; pre += down[c];
}
}
{
T pre;
R0F(i,sz(adj[x])) {
int c = adj[x][i]; if (c == par[x]) continue;
up[c] += pre; pre += down[c];
}
}
F0R(i,sz(adj[x])) {
int c = adj[x][i]; if (c == par[x]) continue;
up[c].tran(); dfs2(c);
}
}
T getSub(int x, int y) { return par[x] == y ? down[x] : up[y]; }
void init(int n) {
par[1] = 0; dfs(1); dfs2(1);
FOR(i,1,n+1) {
T p = T(); trav(t,adj[i]) p += getSub(t,i);
ps(p.v);
}
}
};
int main() {
setIO(); int n; re(n,MOD);
SubtreeDP<MX> S;
F0R(i,n-1) {
int a,b; re(a,b);
S.ae(a,b);
}
S.init(n);
}
```
</CPPSection>
</LanguageSection>
</Spoiler>
## Combining Subtrees
(show why complexity is $O(N^2)$)
## Problems
<Info title="Pro Tip">
Don't just dive into trying to figure out a DP state and transitions -- make some observations if you don't see any obvious DP solution! Also, sometimes a greedy strategy suffices.
</Info>
<Problems problems={metadata.problems.usaco} />
<Spoiler title="Ostap & Tree">
```cpp
vmi yes[101], no[101];
int n,k;
vi adj[MX];
void dfs(int x, int y) {
yes[x] = no[x] = {1}; // black, not black
// dist of closest good vertex
// or dist of farthest bad vertex
auto ad = [](vmi& a, int b, mi c) {
while (sz(a) <= b) a.pb(0);
a[b] += c;
};
trav(t,adj[x]) if (t != y) {
dfs(t,x);
yes[t].insert(begin(yes[t]),0);
no[t].insert(begin(no[t]),0);
if (sz(no[t]) > k+1) no[t].pop_back();
vmi YES, NO;
F0R(i,sz(yes[x])) F0R(j,sz(yes[t])) ad(YES,min(i,j),yes[x][i]*yes[t][j]);
F0R(i,sz(no[x])) F0R(j,sz(no[t])) ad(NO,max(i,j),no[x][i]*no[t][j]);
auto yesNo = [&](vmi good, vmi bad) {
F0R(i,sz(good)) F0R(j,sz(bad)) {
if (i+j <= k) ad(YES,i,good[i]*bad[j]);
else ad(NO,j,good[i]*bad[j]);
}
};
yesNo(yes[x],no[t]); yesNo(yes[t],no[x]);
swap(yes[x],YES); swap(no[x],NO);
}
}
int main() {
setIO(); re(n,k);
F0R(i,n-1) {
int a,b; re(a,b);
adj[a].pb(b), adj[b].pb(a);
}
dfs(1,0);
mi ans = 0;
trav(t,yes[1]) ans += t;
ps(ans);
// you should actually read the stuff at the bottom
}
```
</Spoiler>