原题:一颗含有n个结点的树,所有的结点依次编号为1,2,3,……,n.对于编号为v的结点,定义t(v)为v的后代中所有编号小于v的结点个数。输入这棵树,请输出t(1),t(2),t(3),……,t(n)。
当然,对于本题,写出一个O(n2)的算法是一件很容易的事,这显然也不是接下来要推出的理想算法。
先深度优先遍历该树,按照访问的先后顺序排列结点:7 10 14 12 13 1 9 11 6 5 8 3 15 12 4(DFS序列)。然后把每个结点的子结点先后顺序反转,重新遍历,可得到新的排序结点序列:7 4 3 12 15 9 6 8 5 11 1 10 14 13 2(逆DFS序列)。以结点9为代表,显然,把两次遍历过程中在结点9之后被访问的点圈出来,得到下图。
不难发现,两个线框的重叠区域,即为结点9的所有后代结点。看到上面的图,估计有组合数学知识的人都能想到容斥原理。上图中除了线框1和2包括的部分,还包括了结点9本身和它的直系祖先结点。
定义f(v,S)表示在S所描述的集合中,结点编号小于v的个数。则对于9号结点,有:
f(9,线框1)+f(9,线框2)+f(9,9的直系祖先)-f(9,整棵树)=f(9,9的后代)
推广到更一般的情况,有
f(v,DFS序列中v之后的部分)+f(v,逆DFS序列中v之后的部分)+f(v,v的直系祖先)-f(v,整棵树)=f(v,v的后代)