### Osama_Alkhodairy's blog

By Osama_Alkhodairy, history, 2 years ago,

I was checking this problem on csacademy, and I came up with the dp combinatorics solution to calculate the number of topological orderings of a directed tree with a fixed root.

First, let me explain this solution. Let's root the tree at node $1$ and calculate $sz_v$ = size of the subtree rooted at $v$. Let $dp_v$ be the number of possible topological orderings of the subtree rooted at $v$. To calculate $dp_v$, let's imagine $v$ has only two children: $x$ and $y$. We can see quite easily that $dp_v = dp_x * dp_y * {sz_x+sz_y \choose sz_x}$. That's because any valid topological ordering of the subtree of $v$ will begin with $v$ itself and then $sz_x+sz_y$ nodes, and because the nodes of $x$'s and $y$'s subtrees are independent, all possible combinations between them are possible. So, we will choose $sz_x$ spots of the $sz_x+sz_y$ spots and put in them all the nodes form $x$'s subtree and put on the rest the nodes from $y$'s subtree. Of course, after assigning how we will combine the two subtrees, we can permute the nodes in these spots as long as these permutations are valid; that's why we are multiplying by $dp_x*dp_y$. What if $v$ has more than two children? We will loop over them and do the same thing accumulatively. The final expression after some reductions looks like this: $dp_v = \frac{(sz_v-1)!}{\prod sz_u!} * \prod dp_u$, where $u$ is a child of $v$. Actually we can reduce this further to $dp_v = \frac{sz_v!}{\prod sz_u}$, where $u$ will loop over all nodes in $v$'s subtree.

I felt really unsatisfied after all these reductions because the final expression didn't make sense on its own. But here's a simpler way to think about it without going through all of this.

The number of valid orderings for the whole tree is $dp_1 = \frac{n!}{\prod_{v=1}^{n} sz_v}$. Here, $n!$ represents all possible permutations. Of course, not all of them are valid. For example, the first element of any valid permutation must be $1$ (the root), and that's why we are dividing by $sz_1$. More generally, let's go through the subtrees from the largest to the smallest and perform the division. When considering a subtree, the root of that subtree must appear before all the other nodes from the subtree in the permutation. As we are going over the subtrees from the largest to the smallest, all the permutations of the current subtree contribute to the answer. However, not all of them are valid; we want to fix the root of the subtree in the beginning, so we divide by $sz_v$, so that, now, the current subtree is contributing to the answer by a factor of $(sz_v-1)!$, instead of a factor of $sz_v!$ (because we are fixing one of the nodes).

I found this way of thinking about it much more insightful than the way with reductions, so I wanted to share it with you.

• +88

 » 2 years ago, # |   +32 orz
 » 15 months ago, # | ← Rev. 3 →   -185 Awesome.
 » 14 months ago, # | ← Rev. 2 →   -65 nice explanation it helped me a lot
 » 14 months ago, # |   -30 thank you bro . Nice one
 » 11 months ago, # |   +24 Similar problem or can say exact problem : https://leetcode.com/problems/count-ways-to-build-rooms-in-an-ant-colony/
•  » » 11 months ago, # ^ |   +7 Also this problem uses the same idea https://codingcompetitions.withgoogle.com/codejam/round/0000000000435915/00000000007dc20c
•  » » 11 months ago, # ^ |   -7 Wow really good problem, learnt something new. If anyone is interested in implementation that follows this article : Codeclass Solution { public: long long mod = 1000000007; void dfs(int v, int p, vector> &g, vector &dp, vector &sz, vector &fac, vector &facInv){ bool ok = true; for(int to : g[v]){ if(to!=p){ ok=false; dfs(to,v,g,dp,sz,fac,facInv); sz[v]+=sz[to]; } } if(ok){ return; } long long tot = 0; for(int to : g[v]){ if(to!=p){ dp[v] = ((((dp[v]%mod)*(dp[to]%mod))%mod)*(facInv[sz[to]]%mod))%mod; tot+=sz[to]; } } dp[v] = ((dp[v]%mod)*(fac[tot]%mod))%mod; } int waysToBuildRooms(vector& prevRoom) { int i,n = (int)prevRoom.size(); vector> g(n); for(i=1;i dp(n,1),sz(n,1); vector fac, facInv, inv; fac = facInv = inv = vector(200010, 1); for (i = 2; i <= 200000; i++){ fac[i] = fac[i-1] * i % mod; inv[i] = mod - inv[mod%i] * (mod/i) % mod; facInv[i] = facInv[i-1] * inv[i] % mod; } dfs(0,-1,g,dp,sz,fac,facInv); return dp[0]; } }; 
•  » » 11 months ago, # ^ |   0 op bro!
 » 11 months ago, # |   +1 ty, related problem: ARC 083-F
 » 7 months ago, # | ← Rev. 2 →   -8 .