import std.algorithm;
import std.array;
import std.ascii;
import std.container;
import std.conv;
import std.math;
import std.numeric;
import std.range;
import std.stdio;
import std.string;
import std.typecons;
void log(A...)(A arg) { stderr.writeln(arg); }
int size(T)(in T s) { return cast(int)s.length; }

immutable eps = 1e-18;

long lcm(long a, long b) {
    return a / gcd(a, b) * b;
}
int sign(long x) { return x == 0 ? 0 : cast(int)(x / abs(x)); }
struct Rational {
    long n, d; // numerator, denominator;
    this(long n, long d) {
        if (d < 0) {
            n *= -1;
            d *= -1;
        }
        long g = gcd(abs(n), d);
        this.n = n / g;
        this.d = d / g;
    }
    this(long n) {
        this(n, 1);
    }
    Rational opBinary(string op)(in Rational x) const if (op == "+" || op == "-") {
        long nd = lcm(d, x.d);
        long nn = mixin("n * (nd / d)" ~ op ~ "x.n * (nd / x.d)");
        return Rational(nn, nd);
    }
    Rational opBinary(string op)(real k) const if (op == "*" || op == "/") {
        return Rational(mixin("n" ~ op ~ "k"), d);
    }
    int opCmp(in Rational x) const {
        return (this - x).n.sign;
    }
}

struct P { long a, b; }

void main() {
    auto N = readln.chomp.to!int;
    auto S = new P[][3];
    foreach (i; 0 .. N) {
        int p; long a, b; readf("%s %s %s\n",&p, &a, &b);
        S[p] ~= P(a, b);
    }
    foreach (p; 0 .. 3) S[p] ~= P(1, 0);
    real[] Z;
    foreach (s; S[0]) {
        Z ~= cast(real)(s.a) / (s.a + s.b);
    }
    sort(Z);
    long ans = 0;
    foreach (x; S[1]) {
        foreach (y; S[2]) {
            auto s = cast(real)(x.a) / (x.a + x.b);
            auto t = cast(real)(y.a) / (y.a + y.b);
            if (! (s + t >= 1) ) continue;
            auto lb = 1 - min(s, t);
            auto mid = 2 - (s + t);
            auto sZ = Z.assumeSorted;
            ans += sZ.lowerBound(mid - eps).length - sZ.lowerBound(lb - eps).length;
            ans += sZ.upperBound(mid + eps).length;
        }
    }
    writeln(ans);
}