Extracting values with multiple keys

In response to a), you can write Merge[Rule @@@ data, Identity], which is slightly simpler.

In response to b), there are two different ways to do this nicely. One of which works in 10.0.0, the other will have to wait for 10.0.1.

In 10.0.0 we can use GroupBy to associate each unique key with the set of corresponding values:

grouped = GroupBy[data, First -> Last];

Now do lookups by writing, e.g.:

grouped[25]

or

Catenate @ Lookup[grouped, {25, 73}]

You can also use PositionIndex. Unfortunately 10.0.0 has a slow implementation of PositionIndex as described here. But this is fixed in 10.0.1, takes a fraction of a second to complete on your example for n=6. It's actually faster than the GroupBy code above.

keyindex = PositionIndex[keys];

Now we can easily look up the positions for which the key was 25, and from that get the corresponding values:

Part[vals, keyindex[25]]

To look up both 25 and 73 we just do:

Part[vals, Catenate @ Lookup[keyindex, {25, 73}]]

You can use the ResourceFunction GroupByList to do this. Your example:

n = 10^5;
keys = RandomInteger[{1, 100}, n];
vals = RandomReal[{0, 1}, n];

data = Transpose[{keys, vals}];

Comparison:

r1 = GroupBy[data, First -> Last]; //AbsoluteTiming
r2 = Merge[Rule @@@ data, Identity]; //AbsoluteTiming
r3 = ResourceFunction["GroupByList"][vals, keys]; //AbsoluteTiming

r1 === r2 === r3

{0.053079, Null}

{0.395158, Null}

{0.002779, Null}

True

Also, GroupByList is faster than PositionIndex:

PositionIndex[keys]; //AbsoluteTiming

{0.010389, Null}