For the most part, we accomplished this through cookies. If the cookie exists, read in a hashmap of activities you've recently performed, and update the page DOM based on those. If the cookie didn't exist, then go ahead and do a full query from the db, repop the cache and build the cookie.
That said, we noticed that in a lot of cases, that wasn't ideal, and we started rendering partial templates -- so instead of loading a whole page from cache, we would load page parts, basically one part for each article. This worked well, as each template was basically a pre-rendered static file, and we only had two basic states to work with -- whether the user had voted on this before or not.
Our new strategy then was to query just the indexed table for a user's recent votes, separately query an index for which articles to display, and load the templates for the to-be-displayed articles based upon the user's vote state.
That allowed the page to render a lot faster, but kept the box under more load. Though we suspect it would have scaled well enough (had the project not eventually fizzled out), it wasn't ideal server-side, though delivered a vastly superior end-user experience.
YMMV.
You could load a generic page and then apply their votes by AJAX or something but that doesn't strike me as being any more efficient for the DB than just generating the page.