The @Bluefleets (+1) approach looks good in terms of performance for a dataset; I think that with 500 million records, the performance profile will change.
I think OP wants to get the result in a slightly different format, something that adapts slightly to @Bluefleet code:
select * from #x where handid in ( select handid from #x where cardid in (select cardid from #winners) group by handid having count(handid) = (select count(distinct cardid) from #winners) ) and cardid in (select cardid from #winners)
I would also consider a solution to cursor smoke - since it can work better on a massive number of records depending on the data structure, indexes, number of winners, etc.
But without a complete dataset, which I can not say.
source share