I don't see why np.where shouldn't work here:
>>> np.where((ar3==1)[:, None],
... ar2 // ar2[:, [0]], # where condition is True, divide by first column
... ar2 // ar2[:, [4]]) # where condition is False, divide by last column
array([[ 1, 0, 0, 0, 0],
[ 1, 0, 0, 0, 0],
[ 7, 6, 4, 2, 1],
[11, 8, 6, 3, 1],
[ 1, 0, 0, 0, 0]])
I'm using Python 3 that's why I used // (floor division) instead of regular division (/) otherwise the result would contain floats.
This computes the arrays eagerly, so it evaluates ar2 // ar2[:, [0]] and ar2 // ar2[:, [4]] for all values. Effectively holding 3 arrays of the size of ar2 in memory (the result and the two temporaries). If you want it more memory-efficient you need to do apply the mask before doing the operation:
>>> res = np.empty_like(ar2)
>>> mask = ar3 == 1
>>> res[mask] = ar2[mask] // ar2[mask][:, [0]]
>>> res[~mask] = ar2[~mask] // ar2[~mask][:, [4]]
>>> res
array([[ 1, 0, 0, 0, 0],
[ 1, 0, 0, 0, 0],
[ 7, 6, 4, 2, 1],
[11, 8, 6, 3, 1],
[ 1, 0, 0, 0, 0]])
This computes only the necessary values which uses less memory (and is probably faster too).