import std.stdio, std.string, std.algorithm, std.array, std.range, std.conv,
    std.typecons, std.math, std.container, std.format;

// from https://ei1333.github.io/luzhiled/snippets/structure/union-find.html
class UnionFind
{
public:
    long[] par;

    this(long size)
    {
        par = new long[](size);
        foreach (i; 0 .. size)
        {
            par[i] = -1;
        }
    }

    long size(long x)
    {
        return -(par[find(x)]);
    }

    long find(long x)
    {
        if (par[x] < 0)
        {
            return x;
        }
        par[x] = find(par[x]);
        return par[x];
    }

    bool same(long x, long y)
    {
        return !unite(x, y);
    }

    bool unite(long x, long y)
    {
        x = find(x);
        y = find(y);

        if (x == y)
        {
            return false;
        }
        if (par[x] > par[y])
        {
            par[y] += par[x];
            par[x] = y;
        }
        else
        {
            par[x] += par[y];
            par[y] = x;
        }
        return true;
    }
}

void main(string[] args)
{
    long n = readln.strip.to!long;
    alias T = Tuple!(int, int);
    T[][] xss = [];
    foreach (i; 0 .. n)
    {
        long m = readln.strip.to!long;
        T[] xs = [];
        foreach (j; 0 .. m)
        {
            int x, y;
            readf("%d %d\n", &x, &y);
            xs ~= T(x, y);
        }
        xss ~= xs;
    }

    long ans = 0;
    auto uf = new UnionFind(n + 1); // n + 1人いる。0番は必ず不親切
    // 順番にi人目を正直者と仮定してみる
    foreach (i; 0 .. n)
    {
        int[] honesters = [];
        foreach (xs; xss[i])
        {
            if (xs[1] == 0)
            {
                uf.unite(xs[0], 0);
            }
            else
            {
                uf.unite(xs[0], i + 1); // i + 1は正直者と仮定した人。
                honesters ~= xs[0] - 1;
            }
        }

        int[] used = [cast(int) i];
        bool honesty = true;

        // 正直者と思われる人の証言を辿って矛盾がないことを確かめる
        while (honesters.length != 0 && honesty)
        {
            auto j = honesters.front;
            honesters.popFront();
            used ~= j;

            foreach (xs; xss[j])
            {
                if (xs[1] == 0)
                {
                    if (uf.same(xs[0], i + 1))
                    {
                        honesty = false;
                        break;
                    }
                    else
                    {
                        uf.unite(xs[0], 0);
                    }
                }
                else
                {
                    if (uf.same(xs[1], 0))
                    {
                        honesty = false;
                        break;
                    }
                    else
                    {
                        uf.unite(xs[0], i + 1);
                        if (!used.canFind(xs[0] - 1))
                        {
                            honesters ~= xs[0] - 1;
                        }
                    }
                }
            }
        }

        if (honesty)
        {
            ans = max(ans, used.length);
        }
    }
    writeln(ans);
}
