I think the trick here is to use named branches.
For each user create a seperate named branch. When they push/pull between their repos and anyone elses they get all the changesets other people have committed, but they will only update to their own branch's changesets and commit into their own branch.
If a user wants to incorporate changes from another user, then they merge that changeset into their own branch, backing out any changesets they don't actually want.
--> B1 ---> B2 ---> B3
A1 ---> A2 ---> A3 ---> A4 ---> A5 ---> A6
--> C1 ---> C2 ---> C3 ---> C4
The user of branch A wants the changes B added in revisions B1 & B2, so merges B2 into A3, comitting it as A4. User B doesn't want A's changes though, so doesn't merge in A3 or A4 and just creates a new B3.
User C wants all of the changes from A4, so merges it into C3 to create C4.
User A however, wants everything C did apart from C1, so A merges C3 into A4, creating A5 and then uses back-out to undo the changes in C1, creating A6. From then on, whenever the C branch is merged into the A branch it will be missing the changes in C1.
User C now has to be careful though. If A6, or one of it's decendents, is merged back in to C4, then the backout of C1 will be merged in too.
Hope this helps,
Edit: For more info on Mercurial branching and merging, you could do worse than check out Steve Losh's blog entry.
Of those options listed, the clone technique is the easiest to get started with, but all it takes is one inappropriate pull and you stop being able to tell which parts of the branch are which and it all gets confusing. Named branches make it much easier once you get the hang of them.