It turns out that it is possible to do what I want with linear preprocessing and constant delay, by an argument suggested by my friend Louis. Here is my summary of his idea.
Edit: The scheme below, in a much more concise and understandable form, appears as Theorem C.1 in our recent preprint. I'm leaving the messy sketch below in case it's useful, but you should probably look at the preprint instead (the theorem and proof are independent from the surrounding material). The proof is generalized to multitrees and also refines the scheme slightly to show a logarithmic bound on memory usage.
Let's consider a polytree $P$. The linear preprocessing can insert
intermediate nodes and collapse nodes with
only one child to get a polytree which we can use to answer queries on the original polytree, so that we can assume
without loss of generality that all internal nodes of $P$ have degree exactly
2.
We will do the linear-time preprocessing bottom-up following any
topological order. For every node of $P$ that we process (whose children have
all already been processed), we will construct an index structure for
the node, which we call an "infix tree", and which may also include
pointers to other previously defined such structures (but not modify
them, of course: one can think of this as a purely functional
data structure supporting constant-delay enumeration and constant-time
union.)
The construction will work in linear time, and for any node we will be
able to enumerate its descendants in constant delay by considering its
infix tree.
There are three kinds of nodes in infix trees:
- leaves, which are labeled with an explicit set of at least one and at
most four elements (which are leaves of the original polytree);
- small internal nodes, which are labeled with one explicit element
and with pointers to one or two infix tree nodes;
- large internal nodes, which are labeled with two explicit elements and
with pointers to one or two infix tree nodes.
We further require that there are no duplicate elements in the infix
tree, i.e., for every node $x$ of the infix tree, each leaf of $P$ appears
at most once in the labels of $x$ and its descendants. A node $x$ in
an infix tree codes a set $S(x)$ of leaves of $P$,
namely, those which appear in the node's label and its descendants (and
as we just assumed it is always duplicate-free). The idea of the infix
tree is that, by keeping around some explicit elements, we can both use
them when enumerating to make progress when visiting the nodes, and use
them when unioning two sets of descendants to have sufficiently many
elements to annotate the newly created nodes in the infix tree.
Our indexing data structure will map each node $n$ of the polytree to an
infix tree node $N(n)$ capturing exactly its reachable leaves, i.e.,
$S(N(n))$ is the set of leaves that are reachable from $n$ in $P$.
There are two claims:
- we can actually do this in linear time;
- when we have done this, we can enumerate in constant delay.
Let's first see why claim 2 is true. From the infix tree $N(n)$ of a node $n$ of
$P$, we can enumerate its contents in constant delay with the
following algorithm. Maintain a working list $W$ of nodes of the infix
tree to be enumerated, initially it is $\{N(n)\}$. Now, until $W$ is empty,
take its first element $x$ and do the following:
- if $x$ is a leaf of the infix tree, enumerate its elements and
discard $x$
- if $x$ is an internal node (small or large), enumerate its explicit
elements and add its child nodes to $W$.
It is clear that this correctly enumerates $S(N(n))$, and that it is constant
delay because at each step we enumerate something
So now let's turn to the proof of claim 1. To do this, we will proceed
bottom-up as I pointed out above, further maintaining the following
invariant: for any node $n$ of $P$, $N(n)$ is never a small internal node, so it is
either a large internal node or a leaf. In other words, small internal
nodes will only be used as an intermediate tool in the construction, but
no node of $P$ directly points to such a node in the result of the indexing.
The base case is for a leaf $n$ of $P$, where we set $N(n)$ to be a
fresh leaf node of the infix tree whose contents are $\{n\}$.
The inductive case is for an internal node $n$ of $P$ with
children $n_1$ and $n_2$, for which $N_1 := N(n_1)$ and $N_2 := N(n_2)$ have
already been prepared (and are not small internal nodes). We set $N(n)$ as
follows depending on the types of $N_1$ and $N_2$. Note that, by design, in
the nodes that we add, we will be pointing to $N_1$ and $N_2$ (and also to
their children in some cases), but that's fine provided that we don't
modify them.
If $N_1$ and $N_2$ are leaves and their explicit lists contain at most
four elements total, set $N(n)$ to be a fresh leaf node with an
explicit list which is the union of the labels of $N_1$ and $N_2$.
If $N_1$ and $N_2$ are leaves and their explicit lists contain five or
more elements total, set $N(n)$ to be a fresh large internal node
labeled with the first two elements of the union of the labels, and
whose two children are two fresh leaves with the missing elements
(the two lists can be made non-empty because there are at least two
elements left).
If $N_1$ is a large internal node and $N_2$ is a leaf, we make $N$ a fresh
large internal node labeled with the elements of $N_1$, and having one
child which is a fresh small internal node labeled with the first
element of $N_2$ and whose children are (pointers to) the children of
$N_1$. If $N_2$ has strictly more than one element, we create a fresh leaf
node containing the remaining elements of $N_2$, that we add as a second
child of $N$.
The case where $N_1$ is a leaf and $N_2$ is a large internal node is
symmetric to the case above.
If $N_1$ and $N_2$ are large internal nodes, we create a fresh large
internal node $N$ containing the elements of $N_1$, and whose children are
two fresh small internal nodes, the first one containing the first
element of $N_2$ and pointers to the children of $N_1$, the second one
containing the second element of $N_2$ and pointers to the children of
$N_2$.
This is clearly in constant time, and one can verify that the semantics
is correct, i.e., $S(N)$ is the set of reachable leaves, with no dupes:
this is obvious for leaves and proved in the inductive case from the
contents of $N_1$, $N_2$ and their children. This concludes the proof of claim 2 and the proof of the overall scheme.