Finally found it. In addition to the content type id, you also need to set the name of the content type:
listItem["ContentType"] = "MyContentType";"MyContentTypeName";
I can't explain why it actually works though, I'd need to look at how the event receivers are resolved. I found the answer in the SaveButton webcontrol's source.