Precomputation for partial products in monoids
As you say, with $O(n)$ precomputation you can arrange that each access takes $O(\log n)$ time.  You build a binary tree over the elements, and annotate each internal node with the product of the leaves under it.  This takes $O(n)$ precomputation and $O(n)$ storage.

If you are willing to do more precomputation, you can reduce the access time.  In particular, with $O(n \log n)$ precomputation and $O(n \log n)$ storage, you can arrange that each access runs in $O(1)$ time.  The idea is outlined below.



Notationally, we will let $b_{i,j}$ denote the product $a_i a_{i+1} \cdots a_j$, as you define in your question.

First, precompute the products $b_{i,n/2-1}$ for $i=1,2,\dots,n/2-1$ and the products $b_{n/2,j}$ for $j=n/2,\dots,n-1,n$.  This can be done with a $O(n)$ precomputation.  This will let you compute any product $b_{i,j}$ in $O(1)$ time if we have $i &lt; n/2 \le n/2$, i.e., if the range $[i,j]$ spans the midpoint.

We still need a way to handle ranges that don't span the midpoint.  We'll handle that recursively.  Basically, take the sequence $a_1,\dots,a_{n/2-1}$ and recursively build a data structure for it (e.g., find its midpoint $a_{n/4}$, etc.).  Also, recursively build a data structure for $a_{n/2},\dots,a_n$.  This will let us compute all products $b_{i,j}$ where $j&lt;n/2$ or $i\ge n/2$, i.e., where the range $[i,j]$ doesn't span the midpoint.  This covers all the cases.

How much time does the precomputation take?  If $T(n)$ denotes the time for the entire precomputation, it satisfies the recurrence

$$T(n) = 2 T(n/2) + O(n),$$

which solves to $T(n) = O(n \log n)$.  Similarly, we can see that the amount of storage needed is also $O(n \log n)$.  Finally, this data structure will let you compute any product $b_{i,j}$ in $O(1)$ time.

(I'll let you figure out how to compute $b_{i,j}$ from this data structure in $O(1)$ time.  It can be done with some clever bit-shifting tricks.  It may help to think about the data structure in terms of the big-endian binary representation of the indices.  For each index $i$, we compute $b_{i,i'}$ where $i,i'$ share a common prefix and then $i'$ is all ones after the common prefix; and we compute $b_{j',j}$ where $j',j$ share a common prefix and then $j'$ is all zeros after the common prefix.  Given $i,j$, we can find the longest common prefix of $i,j$, then express as the product $b_{i,j} = b_{i,i'} b_{j',j}$, look up the values of $b_{i,i'}$ and $b_{j',j}$, and compute the product.  All of these can be done in $O(1)$ time by placing the precomputed values in an array in the correct order.)