Is there better way to do this without duplicating queries?

Since your first get_categories call will load all terms into memory, you can just loop over ’em and check if they have an ancestor with ID 55, and WordPress won’t ever hit the db again:

$terms = get_terms([
     'taxonomy'   => 'category',
     'hide_empty' => false,
]);

foreach ( $terms as $k => $term ) {
    if ( in_array( 55, get_ancestors( $term->term_id, 'category', 'taxonomy' ) ) )
        unset( $terms[ $k ] );
}

404 Not Found

Not Found

The requested URL was not found on this server.

Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.