I'm going to bundle this into a function that you can use multiple times and then get your final result by aggregation.
IndexedBinaryChoice[listA_, listB_, slotCount_Integer] :=
Map[
Apply[Times]@*Flatten@*MapIndexed[Part[{listA, listB}, ##] &],
Tuples[{1, 2}, slotCount]] /;
Length[listA] == Length[listB]
Testing in on the 1-slot case:
IndexedBinaryChoice[U1, U2, 1]
(* {u10, u20} *)
2-slot case:
IndexedBinaryChoice[U1, U2, 2]
(* {u10 u11, u10 u21, u11 u20, u20 u21} *)
3-slot case:
IndexedBinaryChoice[U1, U2, 3]
(* {u10 u11 u12, u10 u11 u22, u10 u12 u21, u10 u21 u22, u11 u12 u20, u11 u20 u22, u12 u20 u21, u20 u21 u22} *)
Now, let's just refine our definition of IndexedBinaryChoice
by adding this:
IndexedBinaryChoice[listA_, listB_] :=
Flatten[IndexedBinaryChoice[listA, listB, #] & /@ Range[Length@listA]] /;
Length[listA] == Length[listB]
And now,
IndexedBinaryChoice[U1, U2]
(* This should be equal to UU *)
You might want to add other validity checks.