Since your comparison function is quite complex, I've rewritten it very verbosely, so each possible case can be inspected separately. I've also separated into a different function the decision about which partition of the output each element goes into.
#include <algorithm>
#include <iostream>
#include <iterator>
int classify(int const i)
{
    if (i%3==0 && i%2!=0) {
        return 1;
    }
    else if (i%3!=0) {
        return 2;
    }
    else {
        return 3;
    }
}
bool comp(int const a, int const b)
{
    int const a_part = classify(a);
    int const b_part = classify(b);
    if (a_part==1) {
        if (b_part==1) {
            // both in first partition, order ascending
            return a<b;
        }
        else {
            // a in first partition, b not, so a always first
            return true;
        }
    }
    else if (a_part==2) {
        if (b_part==1) {
            // b comes before a
            return false;
        }
        else if (b_part==2) {
            // both in middle partition, order ascendingly
            return a<b;
        }
        else {
            // a in middle partition, b in last partition, so a always first
            return true;
        }
    }
    else { // (a_part==3)
        if (b_part!=3) {
            // a in last partition, b in first or middle partition,
            // so b always comes first
            return false;
        }
        else {
            // both in last partition, order descending
            return b<a;
        }
    }
}
int main()
{
    int ar[8] = {18 ,5 ,24 ,9 ,12 ,6 ,2, 3};
    std::sort(std::begin(ar),
              std::end(ar),
              comp);
    std::copy(std::begin(ar),
              std::end(ar),
              std::ostream_iterator<int>(std::cout,
                                         "\n"));
}
Output:
$ ./SO 
3
9
2
5
24
18
12
6
Bear in mind your comparator must induce a Strict Weak Ordering, which I think this does, but it's a bit trickier than one would normally use for sorting.
Always write your code as clearly as possible, not as short as possible. If you have some complicated logic, break it up, move parts out into functions, and add plenty of comments. Remember, other people have to read and understand it, including yourself in 6 months.
What might be a better approach is to split the sort up. You are really talking about splitting the array into 3 partitions, each being treated differently. So use std::partition twice, and std::sort three times. I think that may well be more understandable. This code has the exact same output as the above:
bool isOddMultipleOfThree(int const i)
{
    return (i%3==0 && i%2!=0);
}
bool isEvenMultipleOfThree(int const i)
{
    return (i%3==0 && i%2==0);
}
int main()
{
    int ar[8] = {18 ,5 ,24 ,9 ,12 ,6 ,2, 3};
    // split off the first partition
    auto firstSplit = std::partition(std::begin(ar),
                                     std::end(ar),
                                     isOddMultipleOfThree);
    // sort the first partition
    std::sort(std::begin(ar),
              firstSplit,
              std::less<int>());
    // split off end partition
    // use a lambda to invert the predicate, because we want the matching
    // values pushed to the end
    auto secondSplit = std::partition(firstSplit,
                                      std::end(ar),
                                      [](int const i) {
                                        return !isEvenMultipleOfThree(i);
                                      });
    // sort middle partition
    std::sort(firstSplit,
              secondSplit,
              std::less<int>());
    // sort last partition
    std::sort(secondSplit,
              std::end(ar),
              std::greater<int>());
    // print
    std::copy(std::begin(ar),
              std::end(ar),
              std::ostream_iterator<int>(std::cout,
                                         "\n"));
}
Also worth mentioning, many people (myself included) consider using namespace std; and std::endl are bad practices.