According to the codex for WP_Query, the solution might be to use the post_parent
as the id of the top level post. But as there are a whole lotta top level cpts in your case, I think this solution should help you: How to display only top level posts in loop via WP_Query?.
Update:
<?php
$all = get_posts(array('post_type'=> 'city', 'posts_per_page' => -1));
$parents = array();
foreach ($all as $single)
{
$kids = get_children($single->ID);
if(isset($kids) && !empty($kids) && count($kids) >= 1)
{
$parents[] = $single->ID;
}
}
$args = array('post_type' => 'city', 'orderby' => 'name', 'order' => 'ASC', 'posts_per_page' => -1, 'post__not_in' => $parents );
$city = new WP_Query($args);
while ($city->have_posts() ) : $city->the_post();
echo get_the_title()."<br />";
endwhile;
wp_reset_postdata();
?>
This is a long cut, but it works. Just make sure you check if the $parents array is not empty before including it in the query.