DP on induced subgraphs of a tree
An induced subgraph of a graph is another graph, formed from a subset of the vertices of the graph and all of the edges connecting pairs of vertices in that subset. You are given an unrooted tree, find the number of induced subgraphs that are also trees.
For an induced subgraph to satisfy the properties of a tree, it is necessary and sufficient for it to be connected (assuming that the original graph was a tree).
Hence, from now onwards, whenever we talk about induced subgraphs, we will implicitly mean connected induced subgraphs.
The given tree is unrooted. Let’s root the tree at vertex
The first DP defintion that comes to mind is
represents the number of connected induced subgraphs when the resulting subgraph is a tree rooted at node .
However, this DP defintion doesn’t make a lot of sense, because even if you assume the tree to be rooted at vertex
Let’s assign each node a height when viewed from the ground. For example, the root is the highest vertex, all children of root are the second highest vertex and so on. The deepest leaf vertices are the lowest vertices in this ordering.
Then, we can define
is the number of connected induced subgraphs when the highest vertex in the subgraph is node .
Then, the answer to the original problem would be
Luckily, there cannot exist 2 highest vertices
With this DP definition in mind, let us analyze the transitions. We will assume that the DP values of all children have been computed (since the function is called recursively for each children). We need to figure out how these answers can be combined to compute
We will start adding children one by one. Suppose we’ve added
You can either delete the edge between
If you decide to delete the edge, then the number of ways remain the same, i.e,
dfs(u, par):
dp[u] = 1
for c in adj[u]:
if c == par:
continue
dfs(c, u)
dp[u] += dp[u]*dp[c]
The time complexity is
You can find the full code here
Given a tree, find the number of connected induced subgraphs where vertex
has degree .
Root the tree at vertex
represents the number of connected induced subgraphs where node is the highest vertex and has degree .
Let us recursively compute the DP value for the children and then incrementally add children. Not only that, we will add children degree by degree. This concept of adding children degree by degree also appeared in the previous blog.
Suppose you have a configuration where node
- You can delete the edge between
and . You get a configuration where vertex has degree . - You can keep the edge between
and . Then, in the resulting subgraph, the degree of both and increases by 1. Therefore, we get a configuration where the degree of node is .
Of course, to avoid mixing old configurations of degree
dfs(u, par):
dp[u][0] = 1
for c in adj[u]:
if c == par:
continue
dfs(c, u)
ndp = []
for d in range(adj[u].size)
# Ignore this children
ndp[d] += dp[u][d]
for now in range(adj[c].size):
nxt = d + 1
# Append this children
ndp[nxt] += dp[u][d]*dp[c][now]
swap(dp[u], ndp)
The time complexity is
Resources for the proof can be found here. I will add my own interpretation later.
You can find the full code here
Given a tree, find the number of connected induced subgraphs where vertex
has degree or or .
In this version, we don’t need to print the answer for all the degrees. We can still compute the
Note that all the degree
is the number of connected induced subgraphs where vertex is the highest vertex and has degree
The transitions are same as before. When you are combining vertex
dfs(u, par):
dp[u][0] = 1
for c in adj[u]:
if c == par:
continue
dfs(c, u)
ndp = []
for d in {0, 1, 2}:
# Ignore this children
ndp[d] += dp[u][d]
for now in {0, 1, 2}:
nxt = d + 1
if nxt > 1:
nxt = 2
# Append this children
ndp[nxt] += dp[u][d]*dp[c][now]
swap(dp[u], ndp)
The time complexity is
You can find the full code here
Given a tree, find the number of connected induced subgraphs where all the degree-1 vertices are monochromatic.
We will solve this problem for each color independently using the ideas discussed above.
Fix a color.
is the number of connected induced subgraphs where the highest vertex is node , it has degree and all the degree-1 vertices except node are monochromatic with the given color.
The DP definition might seem odd at first. For example, why isn’t
The answer lies in the fact that in Tree DP problems, there would be a correction when you push the DP values up the tree. For example, even if vertex
However, if a child has degree
You can read more about the extension and correction phase from the previous blog.
This is not a new idea. You might have implicitly used it in other DP problems. The intermediate DP values might look contrary to what’s being asked in the problem, but we still retain those in the hopes of it being corrected in later stages.
dfs(u, par, color):
dp[u][0] = 1
for c in adj[u]:
if c == par:
continue
dfs(c, u)
ndp = []
for d in {0, 1, 2}:
# Ignore this children.
ndp[d] += dp[u][d]
for now in {0, 1, 2}:
# Last correction for child.
if now is 0 and a[child] != color:
continue
nxt = d + 1
if(nxt > 1) {
nxt = 2
}
# Append this children.
ndp[nxt] += dp[u][d]*dp[c][now]
swap(dp[u], ndp)
To compute the answer for a single color, we can iterate over all vertices and sum up the contribution of degree-2 states. However, for degree 0 and degree 1 states, we should only sum up the contribution if the color of the node is equal to the current color being investigated. This is because no more corrections are left for the highest vertex, and if the colors are not matched yet, we have to discard the configuration.
for color in 1 to n:
ans = 0
dfs(1, -1, color)
for u in 1 to n:
if color == u.color:
ans += dp[u][0] + dp[u][1]
ans += dp[u][2]
print(ans)
The time complexity for a single color is
You can find the full code here
This tutorial introduces several new ideas:
- How to extend a tree by incrementally adding children.
- How to compress multiple states into a single state.
- Why and how to maintain DP values that are invalid according to the problem description.
- How to perform DP on highest vertices for unrooted tree.
- How to avoid polluting DP values of the current level by maintaining an auxillary DP array for the next level.
If you are looking for a problem to test your understanding of the first 3 concepts, you can attempt ABC248F: Keep Connect. Please do not look at its editorial, I can assure you that you’ll be able to come up with the solution on your own with a little bit of help. Ask me for hints on any of these platforms.
If you’re interested, you can checkout my youtube channel.
I’m sure there are alternate (possibly easier) versions of the