As others said, MySQL is probably not the best choice for this task. If you want to use it anyway, here is a possible solution using Nested set model. Extend table categories with two additional integer fields, lb and rb, that will keep left and right boundary of the particular category. All subcategories of that category will need to have their interval [lb, rb] completely contained within the parent category interval. Thus, the table categories should look like:
id tree lb  rb
==============
1   0   11  14
2   0   9   10
6   0   1   8
13  1   12  13
16  6   2   3
17  6   4   5
18  6   6   7
and the query that returns your wished result is:
select id,
(select count(*) from categories where lb >= cat.lb and rb <= cat.rb and tree > 0) as num_subcategories,
(select count(*) from ads a join categories c on a.cat_id = c.id where lb >= cat.lb and rb <= cat.rb) as num_ads
from categories cat
where tree = 0;
Here is fiddle. Notice that inserts and deletions of categories would become more complicated, that is a tradeoff to get easy search through the whole hierarchy. Here is a procedure for insert:
drop procedure if exists insert_category;
create procedure insert_category(_id int, _parent_id int) 
begin
    declare _parent_rb int default null;    
    if _parent_id = 0 then
        set _parent_rb = 1;
    else
        select rb from categories where id = _parent_id
        into _parent_rb;
    end if;
    update categories set rb = rb + 2 where rb >= _parent_rb;   
    update categories set lb = lb + 2 where lb >= _parent_rb;
    insert into categories(id, tree, lb, rb) 
    values (_id, _parent_id, _parent_rb, _parent_rb + 1);
end;
To get the table categories filled as described above, just call this procedure multiple times:
call insert_category(1, 0);
call insert_category(2, 0);
call insert_category(6, 0);
call insert_category(13, 1);
call insert_category(16, 6);
call insert_category(17, 6);
call insert_category(18, 6);
HTH.