Problem:
Problem F:
Webby Subway
You are an officer of the Department of Land and Transport in Oykot City. The department has a plan to
build a subway network in the city central of Oykot.
In the plan,
n
subway lines are built, and each line has two or more stations. Because of technical
problems, a rail track between two stations should be straight and should not have any slope. To make
things worse, no rail track can contact with another rail track, even on a station. In other words, two
subways on the same floor cannot have any intersection.
Your job is to calculate the least number of required floors in their plan.
Input
The input consists of multiple datasets. Each dataset is formatted as follows:
N
Line
1
Line
2
...
Line
N
Here,
N
is a positive integer that indicates the number of subway lines to be built in the plan (
N
≤ 22),
and
Line
i
is the description of the
i
-th subway line with the following format:
S
X
1
Y
1
X
2
Y
2
...
X
S
Y
S
S
is a positive integer that indicates the number of stations in the line (
S
≤ 30), and (
X
i
,
Y
i
) indicates
the coordinates of the
i
-th station of the line (-10000 ≤
X
i
,
Y
i
≤ 10000). The rail tracks are going to
be built between two consecutive stations in the description. No stations of the same line have the same
coordinates.
The input is terminated by a dataset of
N
= 0, and it should not be processed.
Output
For each dataset, you should output the least number of required floors.
Sample Input
2
2
0 0
10 0
2
0 10
10 10
2
2
0 0
10 10
2
0 10
10 0
3
2
0 0
10 10
2
0 10
10 0
2
1 0
1 10
0
Output for the Sample Input
1
2
3

Submitted solutions:

code_snippet: //
// 彩色数を求める O(N2^N) のアルゴリズム
//
// cf.
//   https://drken1215.hatenablog.com/entry/2019/01/16/030000
//
// verified
//   ARC 171 D - Rolling Hash
//     https://atcoder.jp/contests/arc171/tasks/arc171_d
//
//   AOJ 2136 Webby Subway
//     http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2136
//


#include <bits/stdc++.h>
using namespace std;


// modint
template<int MOD> struct Fp {
    // inner value
    long long val;
    
    // constructor
    constexpr Fp() : val(0) { }
    constexpr Fp(long long v) : val(v % MOD) {
        if (val < 0) val += MOD;
    }
    constexpr long long get() const { return val; }
    constexpr int get_mod() const { return MOD; }
    
    // arithmetic operators
    constexpr Fp operator + () const { return Fp(*this); }
    constexpr Fp operator - () const { return Fp(0) - Fp(*this); }
    constexpr Fp operator + (const Fp &r) const { return Fp(*this) += r; }
    constexpr Fp operator - (const Fp &r) const { return Fp(*this) -= r; }
    constexpr Fp operator * (const Fp &r) const { return Fp(*this) *= r; }
    constexpr Fp operator / (const Fp &r) const { return Fp(*this) /= r; }
    constexpr Fp& operator += (const Fp &r) {
        val += r.val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Fp& operator -= (const Fp &r) {
        val -= r.val;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Fp& operator *= (const Fp &r) {
        val = val * r.val % MOD;
        return *this;
    }
    constexpr Fp& operator /= (const Fp &r) {
        long long a = r.val, b = MOD, u = 1, v = 0;
        while (b) {
            long long t = a / b;
            a -= t * b, swap(a, b);
            u -= t * v, swap(u, v);
        }
        val = val * u % MOD;
        if (val < 0) val += MOD;
        return *this;
    }
    constexpr Fp pow(long long n) const {
        Fp res(1), mul(*this);
        while (n > 0) {
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    constexpr Fp inv() const {
        Fp res(1), div(*this);
        return res / div;
    }

    // other operators
    constexpr bool operator == (const Fp &r) const {
        return this->val == r.val;
    }
    constexpr bool operator != (const Fp &r) const {
        return this->val != r.val;
    }
    constexpr Fp& operator ++ () {
        ++val;
        if (val >= MOD) val -= MOD;
        return *this;
    }
    constexpr Fp& operator -- () {
        if (val == 0) val += MOD;
        --val;
        return *this;
    }
    constexpr Fp operator ++ (int) const {
        Fp res = *this;
        ++*this;
        return res;
    }
    constexpr Fp operator -- (int) const {
        Fp res = *this;
        --*this;
        return res;
    }
    friend constexpr istream& operator >> (istream &is, Fp<MOD> &x) {
        is >> x.val;
        x.val %= MOD;
        if (x.val < 0) x.val += MOD;
        return is;
    }
    friend constexpr ostream& operator << (ostream &os, const Fp<MOD> &x) {
        return os << x.val;
    }
    friend constexpr Fp<MOD> pow(const Fp<MOD> &r, long long n) {
        return r.pow(n);
    }
    friend constexpr Fp<MOD> inv(const Fp<MOD> &r) {
        return r.inv();
    }
};

int chromatic_number(const vector<vector<int>> &G) {
    const int MOD = 998244353;
    using mint = Fp<MOD>;
    
    int n = (int)G.size();
    vector<int> neighbor(n, 0);
    for (int i = 0; i < n; ++i) {
        int S = (1<<i);
        for (int j = 0; j < n; ++j) if (G[i][j]) S |= (1<<j);
        neighbor[i] = S;
    }
    
    // I[S] := #. of inndepndet subset of S
    vector<int> I(1<<n);
    I[0] = 1;
    for (int S = 1; S < (1<<n); ++S) {
        int v = __builtin_ctz(S);
        I[S] = I[S & ~(1<<v)] + I[S & ~neighbor[v]];
    }
    int low = 0, high = n;
    while (high - low > 1) {
        int mid = (low + high) >> 1;
        
        // g[S] := #. of "k independent sets" which cover S just
        // f[S] := #. of "k independent sets" which consist of subseta of S
        // then
        //   f[S] = sum_{T in S} g(T)
        //   g[S] = sum_{T in S} (-1)^(|S|-|T|)f[T]
        mint g = 0;
        for (int S = 0; S < (1<<n); ++S) {
            if ((n - __builtin_popcount(S)) & 1) g -= mint(I[S]).pow(mid);
            else g += mint(I[S]).pow(mid);
        }
        if (g != 0) high = mid;
        else low = mid;
    }
    return high;
}



//------------------------------//
// Examples
//------------------------------//

// ARC 171 D
void ARC_171_D() {
    long long P, B, N, M;
    cin >> P >> B >> N >> M;
    
    vector<vector<int>> G(N+1, vector<int>(N+1, 0));
    for (int i = 0; i < M; ++i) {
        int l, r;
        cin >> l >> r;
        --l;
        G[l][r] = G[r][l] = 1;
    }
    if (chromatic_number(G) <= P) cout << "Yes" << endl;
    else cout << "No" << endl;
}


// AOJ 2136 - Webby Subway
using DD = double;
const DD INF = 1LL<<60;      // to be set appropriately
const DD EPS = 1e-10;        // to be set appropriately
const DD PI = acos(-1.0);
DD torad(int deg) {return (DD)(deg) * PI / 180;}
DD todeg(DD ang) {return ang * 180 / PI;}

/* Point */
struct Point {
    DD x, y;
    Point(DD x = 0.0, DD y = 0.0) : x(x), y(y) {}
    friend ostream& operator << (ostream &s, const Point &p) {return s << '(' << p.x << ", " << p.y << ')';}
};
inline Point operator + (const Point &p, const Point &q) {return Point(p.x + q.x, p.y + q.y);}
inline Point operator - (const Point &p, const Point &q) {return Point(p.x - q.x, p.y - q.y);}
inline Point operator * (const Point &p, DD a) {return Point(p.x * a, p.y * a);}
inline Point operator * (DD a, const Point &p) {return Point(a * p.x, a * p.y);}
inline Point operator * (const Point &p, const Point &q) {return Point(p.x * q.x - p.y * q.y, p.x * q.y + p.y * q.x);}
inline Point operator / (const Point &p, DD a) {return Point(p.x / a, p.y / a);}
inline Point conj(const Point &p) {return Point(p.x, -p.y);}
inline Point rot(const Point &p, DD ang) {return Point(cos(ang) * p.x - sin(ang) * p.y, sin(ang) * p.x + cos(ang) * p.y);}
inline Point rot90(const Point &p) {return Point(-p.y, p.x);}
inline DD cross(const Point &p, const Point &q) {return p.x * q.y - p.y * q.x;}
inline DD dot(const Point &p, const Point &q) {return p.x * q.x + p.y * q.y;}
inline DD norm(const Point &p) {return dot(p, p);}
inline DD abs(const Point &p) {return sqrt(dot(p, p));}
inline DD amp(const Point &p) {DD res = atan2(p.y, p.x); if (res < 0) res += PI*2; return res;}
inline bool eq(const Point &p, const Point &q) {return abs(p - q) < EPS;}
inline bool operator < (const Point &p, const Point &q) {return (abs(p.x - q.x) > EPS ? p.x < q.x : p.y < q.y);}
inline bool operator > (const Point &p, const Point &q) {return (abs(p.x - q.x) > EPS ? p.x > q.x : p.y > q.y);}
inline Point operator / (const Point &p, const Point &q) {return p * conj(q) / norm(q);}

/* Line */
struct Line : vector<Point> {
    Line(Point a = Point(0.0, 0.0), Point b = Point(0.0, 0.0)) {
        this->push_back(a);
        this->push_back(b);
    }
    friend ostream& operator << (ostream &s, const Line &l) {return s << '{' << l[0] << ", " << l[1] << '}';}
};

int ccw_for_dis(const Point &a, const Point &b, const Point &c) {
    if (cross(b-a, c-a) > EPS) return 1;
    if (cross(b-a, c-a) < -EPS) return -1;
    if (dot(b-a, c-a) < -EPS) return 2;
    if (norm(b-a) < norm(c-a) - EPS) return -2;
    return 0;
}
bool isinterPS(const Point &p, const Line &s) {
    return (ccw_for_dis(s[0], s[1], p) == 0);
}
bool isinterSS(const Line &s, const Line &t) {
    if (eq(s[0], s[1])) return isinterPS(s[0], t);
    if (eq(t[0], t[1])) return isinterPS(t[0], s);
    return (ccw_for_dis(s[0], s[1], t[0]) * ccw_for_dis(s[0], s[1], t[1]) <= 0 &&
            ccw_for_dis(t[0], t[1], s[0]) * ccw_for_dis(t[0], t[1], s[1]) <= 0);
}

void AOJ_2136() {
    int N;
    while (cin >> N, N) {
        vector<vector<int>> G(N, vector<int>(N, 0));
        vector<vector<Line>> lines(N);
        for (int i = 0; i < N; ++i) {
            int num;
            double x, y;
            cin >> num >> x >> y;
            for (int j = 1; j < num; ++j) {
                double nx, ny;
                cin >> nx >> ny;
                Line l(Point(x, y), Point(nx, ny));
                lines[i].push_back(l);
                x = nx, y = ny;
            }
        }
        for (int i = 0; i < N; ++i) {
            for (int j = i + 1; j < N; ++j) {
                bool ok = false;
                for (auto li : lines[i]) {
                    for (auto lj : lines[j]) {
                        if (isinterSS(li, lj)) ok = true;
                    }
                }
                if (ok) G[i][j] = G[j][i] = true;
            }
        }
        cout << chromatic_number(G) << endl;
    }
}


int main() {
    //ARC_171_D();
    AOJ_2136();
}
accuracy: 1
time_ms: 2470
memory_kb: 19944
final_rank: 3

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

template<uint32_t mod>
int chromatic_number_internal(const vector<int> &es, const vector<int> &ind, int upper) {
  const int N = (int) es.size();
  int ret = upper;
  vector<int> aux(1 << N, 1);
  for (int i = 1; i < ret; i++) {
    uint64_t all = 0;
    for (int j = 0; j < 1 << N; j++) {
      int S = j ^ (j >> 1);
      aux[S] = (1LL * aux[S] * ind[S]) % mod;
      all += j & 1 ? aux[S] : mod - aux[S];
    }
    if (all % mod) {
      ret = i;
      break;
    }
  }
  return ret;
}

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  vector<int> ind(1 << N);
  ind[0] = 1;
  for (int S = 1; S < 1 << N; S++) {
    int u = __builtin_ctz(S);
    ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
  }
  ret = chromatic_number_internal<(int) 1e9 + 7>(es, ind, ret);
//  ret = chromatic_number_internal<(int) 1e9 + 11>(es, ind, ret);
//  ret = chromatic_number_internal<(int) 1e9 + 21>(es, ind, ret);
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 1700
memory_kb: 35968
final_rank: 4

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

template<uint32_t mod>
int chromatic_number_internal(const vector<int> &es, const vector<int> &ind, int upper) {
  const int N = (int) es.size();
  int ret = upper;
  vector<int> aux(1 << N, 1);
  for (int i = 1; i < ret; i++) {
    uint64_t all = 0;
    for (int j = 0; j < 1 << N; j++) {
      int S = j ^ (j >> 1);
      aux[S] = (1LL * aux[S] * ind[S]) % mod;
      all += j & 1 ? aux[S] : mod - aux[S];
    }
    if (all % mod) {
      ret = i;
      break;
    }
  }
  return ret;
}

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  vector<int> ind(1 << N);
  ind[0] = 1;
  for (int S = 1; S < 1 << N; S++) {
    int u = __builtin_ctz(S);
    ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
  }
  ret = chromatic_number_internal<(int) 1e9 + 7>(es, ind, ret);
  ret = chromatic_number_internal<(int) 1e9 + 11>(es, ind, ret);
  ret = chromatic_number_internal<(int) 1e9 + 21>(es, ind, ret);
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 4740
memory_kb: 35984
final_rank: 18

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

template<uint32_t mod>
int chromatic_number_internal(const vector<int> &es, const vector<int> &ind, int upper) {
  const int N = (int) es.size();
  int ret = upper;
  vector<int> aux(1 << N, 1);
  for (int i = 1; i < ret; i++) {
    uint64_t all = 0;
    for (int j = 0; j < 1 << N; j++) {
      int S = j ^ (j >> 1);
      aux[S] = (1LL * aux[S] * ind[S]) % mod;
      all += j & 1 ? aux[S] : mod - aux[S];
    }
    if (all % mod) ret = i;
  }
  return ret;
}

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  vector<int> ind(1 << N);
  ind[0] = 1;
  for (int S = 1; S < 1 << N; S++) {
    int u = __builtin_ctz(S);
    ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
  }
  ret = chromatic_number_internal<(int) 1e9 + 7>(es, ind, ret);
  ret = chromatic_number_internal<(int) 1e9 + 11>(es, ind, ret);
  ret = chromatic_number_internal<(int) 1e9 + 21>(es, ind, ret);
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 4730
memory_kb: 35972
final_rank: 17

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  {
    static constexpr int mod = (int) 1e9 + 7;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod;
        all += j & 1 ? aux[S] : mod - aux[S];
      }
      if (all % mod) ret = i;
    }
  }
  {
    static constexpr int mod2 = (int) 1e9 + 11;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod2;
        all += j & 1 ? aux[S] : mod2 - aux[S];
      }
      if (all % mod2) ret = i;
    }
  }
  {
    static constexpr int mod3 = (int) 1e9 + 21;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod3;
        all += j & 1 ? aux[S] : mod3 - aux[S];
      }
      if (all % mod3) ret = i;
    }
  }
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 4990
memory_kb: 35968
final_rank: 19

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  {
    static constexpr int mod = (int) 1e9 + 7;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod;
        all += j & 1 ? aux[S] : mod - aux[S];
      }
      if (all % mod) ret = i;
    }
  }
  {
    static constexpr int mod2 = (int) 1e9 + 11;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod2;
        all += j & 1 ? aux[S] : mod2 - aux[S];
      }
      if (all % mod2) ret = i;
    }
  }
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 3310
memory_kb: 35856
final_rank: 14

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  for (int d: {11}) {
    int mod = 1e9 + d;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod;
        all += j & 1 ? aux[S] : mod - aux[S];
      }
      if (all % mod) ret = i;
    }
  }
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 1720
memory_kb: 35972
final_rank: 6

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

int chromatic_number(const vector<vector<int>> &graph) {
  int N = (int) graph.size();
  vector<int> es(N);
  for (int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      es[i] |= graph[i][j] << j;
    }
  }
  int ret = N;
  for (int d: {7}) {
    int mod = 1e9 + d;
    vector<int> ind(1 << N), aux(1 << N, 1);
    ind[0] = 1;
    for (int S = 1; S < 1 << N; S++) {
      int u = __builtin_ctz(S);
      ind[S] = ind[S ^ (1 << u)] + ind[(S ^ (1 << u)) & ~es[u]];
    }
    for (int i = 1; i < ret; i++) {
      int64_t all = 0;
      for (int j = 0; j < 1 << N; j++) {
        int S = j ^ (j >> 1);
        aux[S] = (1LL * aux[S] * ind[S]) % mod;
        all += j & 1 ? aux[S] : mod - aux[S];
      }
      if (all % mod) ret = i;
    }
  }
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 1710
memory_kb: 35864
final_rank: 5

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

template<unsigned M>
struct ModInt {
  constexpr ModInt() : _v(0) {}
  constexpr ModInt(long long val) {
    if (val < 0) {
      long long k = (std::abs(val) + M - 1) / M;
      val += k * M;
    }
    assert(val >= 0);
    _v = val % M;
  }

  static constexpr int mod() { return M; }
  static constexpr unsigned umod() { return M; }
  inline unsigned val() const { return _v; }

  ModInt &operator++() {
    _v++;
    if (_v == umod()) _v = 0;
    return *this;
  }
  ModInt &operator--() {
    if (_v == 0) _v = umod();
    _v--;
    return *this;
  }
  ModInt operator++(int) {
    auto result = *this;
    ++*this;
    return result;
  }
  ModInt operator--(int) {
    auto result = *this;
    --*this;
    return result;
  }

  constexpr ModInt operator-() const { return ModInt(umod() - _v); }

  constexpr ModInt &operator+=(const ModInt &a) {
    if ((_v += a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator-=(const ModInt &a) {
    if ((_v += M - a._v) >= M) _v -= M;
    return *this;
  }
  constexpr ModInt &operator*=(const ModInt &a) {
    _v = ((unsigned long long) (_v) * a._v) % M;
    return *this;
  }
  constexpr ModInt pow(unsigned long long t) const {
    if (_v == 0) {
      return 0;  // corner case: 0^0 = ?
    }
    ModInt base = *this;
    ModInt res = 1;
    while (t) {
      if (t & 1) res *= base;
      base *= base;
      t >>= 1;
    }
    return res;
  }

  // https://qiita.com/Mitarushi/items/8d7fb52e8a80e8008463
  constexpr ModInt inv() const {
    long long b = 1, a = _v;
    while (a > 1) {
      long long q = M / a;
      a = M - a * q;
      b = -b * q % M;
    }
    assert(a == 1);  // if a = 0, _v and M are not coprime.
    if (b < 0) b += M;
    ModInt ret;
    ret._v = (unsigned) b;
    return ret;
  }
  constexpr ModInt &operator/=(const ModInt &a) { return *this *= a.inv(); }

  friend constexpr ModInt operator+(const ModInt &a, const ModInt &b) {
    return ModInt(a) += b;
  }
  friend constexpr ModInt operator-(const ModInt &a, const ModInt &b) {
    return ModInt(a) -= b;
  }
  friend constexpr ModInt operator*(const ModInt &a, const ModInt &b) {
    return ModInt(a) *= b;
  }
  friend constexpr ModInt operator/(const ModInt &a, const ModInt &b) {
    return ModInt(a) /= b;
  }
  friend constexpr bool operator==(const ModInt &a, const ModInt &b) {
    return a._v == b._v;
  }
  friend constexpr bool operator!=(const ModInt &a, const ModInt &b) {
    return a._v != b._v;
  }
  friend std::istream &operator>>(std::istream &is, ModInt &a) {
    return is >> a._v;
  }
  friend std::ostream &operator<<(std::ostream &os, const ModInt &a) {
    return os << a._v;
  }

 private:
  unsigned _v;  // raw value
};
const unsigned MOD = int(1e9) + 7;
using Mint = ModInt<MOD>;

int chromatic_number(const vector<vector<int>> &G) {
  int n = (int) G.size();
  vector<int> neighbor(n, 0);
  for (int i = 0; i < n; ++i) {
    int S = (1 << i);
    for (int j = 0; j < n; ++j)
      if (G[i][j]) S |= (1 << j);
    neighbor[i] = S;
  }

  // I[S] := #. of inndepndet subset of S
  vector<int> I(1 << n);
  I[0] = 1;
  for (int S = 1; S < (1 << n); ++S) {
    int v = __builtin_ctz(S);
    I[S] = I[S & ~(1 << v)] + I[S & ~neighbor[v]];
  }
  int low = 0, high = n;
  while (high - low > 1) {
    int mid = (low + high) >> 1;

    // Inclusion-Exclusion Principle.
    //   g[S] := #. of "k independent sets" which cover S just
    //   f[S] := #. of "k independent sets" which consist of subseta of S
    // then
    //   f[S] = sum_{T in S} g(T)
    //   g[S] = sum_{T in S} (-1)^(|S|-|T|)f[T]
    Mint g = 0;
    for (int S = 0; S < (1 << n); ++S) {
      int sign = ((n - __builtin_popcount(S)) & 1) ? -1 : 1;
      g += sign * Mint(I[S]).pow(mid);
    }
    if (g.val() != 0) {
      high = mid;
    } else {
      low = mid;
    }
  }
  return high;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<vector<int>> g(n, vector<int>(n, 0));
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i][j] = g[j][i] = true;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 3540
memory_kb: 19864
final_rank: 12

code_snippet: #include <bits/stdc++.h>
#define REP_(i, a_, b_, a, b, ...) for (int i = (a), END_##i = (b); i < END_##i; ++i)
#define REP(i, ...) REP_(i, __VA_ARGS__, __VA_ARGS__, 0, __VA_ARGS__)
#define ALL(x) std::begin(x), std::end(x)
using Int = long long;
using Uint = unsigned long long;
using Real = double;

template<typename T, typename U>
inline bool chmax(T &a, U b) { return a < b and ((a = b), true); }
template<typename T, typename U>
inline bool chmin(T &a, U b) { return a > b and ((a = b), true); }
template<typename T>
inline int ssize(const T &a) { return (int) a.size(); }
template<typename T>
constexpr T kBigVal = std::numeric_limits<T>::max() / 2;

template<typename T>
inline std::ostream &print_one(const T &x, char endc) {
  if constexpr (std::is_same<T, bool>::value) {
    return std::cout << (x ? "Yes" : "No") << endc;
  } else {
    return std::cout << x << endc;
  }
}
template<typename T>
inline std::ostream &print(const T &x) { return print_one(x, '\n'); }
template<typename T, typename... Ts>
std::ostream &print(const T &head, Ts... tail) {
  return print_one(head, ' '), print(tail...);
}
inline std::ostream &print() { return std::cout << '\n'; }

template<typename Container>
std::ostream &print_seq(const Container &seq,
                        const char *sep = " ",
                        const char *ends = "\n",
                        std::ostream &os = std::cout) {
  const auto itl = std::begin(seq), itr = std::end(seq);
  for (auto it = itl; it != itr; ++it) {
    if (it != itl) os << sep;
    os << *it;
  }
  return os << ends;
}

struct CastInput {
  template<typename T>
  operator T() const {
    T x;
    std::cin >> x;
    return x;
  }
  struct Sized {
    int n;
    template<typename T>
    operator T() const {
      T xs(n);
      for (auto &x: xs) std::cin >> x;
      return xs;
    }
  };
  Sized operator()(int n) const { return {n}; }
} in;

#ifdef MY_DEBUG
#include "debug_dump.hpp"
#include "backward.hpp"
backward::SignalHandling kSignalHandling;
#else
#define DUMP(...)
#define cerr if(false)cerr
#endif

using namespace std;

int chromatic_number(const vector<uint32_t> &g) {
  uint32_t n = (int) g.size();
  const uint32_t nn = 1 << n;
  int ret = 0;
  for (uint32_t b = 0; b < nn; ++b) {
    bool f = true;
    for (uint32_t i = 0; i < n; ++i) {
      if ((b & (1 << i)) && (g[i] & b) != b) {
        f = false;
        break;
      }
    }
    if (f) ret = max(ret, __builtin_popcount(b));
  }
  return ret;
}

typedef complex<Real> P;  // Point

const Real EPS = 1e-9;
// inner product: dot(a,b) = |a||b|cosθ
Real dot(P a, P b) { return (conj(a) * b).real(); }
// outer product: cross(a,b) = |a||b|sinθ
Real cross(P a, P b) { return (conj(a) * b).imag(); }

int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS) return +1;   // counter clockwise
  if (cross(b, c) < -EPS) return -1;  // clockwise
  if (dot(b, c) < -EPS) return +2;    // c--a--b on line
  if (norm(b) < norm(c)) return -2;   // a--b--c on line or a==b
  return 0;                           // a--c--b on line or a==c or b==c
}
bool isecSS(P a1, P a2, P b1, P b2) {
  return ccw(a1, a2, b1) * ccw(a1, a2, b2) <= 0 &&
      ccw(b1, b2, a1) * ccw(b1, b2, a2) <= 0;
}

auto solve(int n) {
  vector<vector<P>> lines(n);
  REP(i, n) {
    int s = in;
    lines[i].resize(s);
    REP(j, s) {
      int x = in, y = in;
      lines[i][j] = {(Real) x, (Real) y};
    }
  }

  auto intersects = [&](int i, int j) -> bool {
    const auto &si = lines[i];
    const auto &sj = lines[j];
    REP(i, 1, si.size()) {
      REP(j, 1, sj.size()) {
        if (isecSS(si[i - 1], si[i], sj[j - 1], sj[j])) {
          return true;
        }
      }
    }
    return false;
  };

  vector<uint32_t> g(n);
  REP(i, n) g[i] |= 1 << i;
  REP(i, n) {
    REP(j, i + 1, n) {
      if (intersects(i, j)) {
        g[i] |= 1 << j;
        g[j] |= 1 << i;
      }
    }
  }
  return chromatic_number(g);
}

int main() {
  std::ios::sync_with_stdio(false), cin.tie(nullptr);
  cout << std::fixed << std::setprecision(18);
  while (true) {
    int n = in;
    if (n == 0) break;
    print(solve(n));
  }
}
accuracy: 1
time_ms: 570
memory_kb: 3376
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;

constexpr double EPS = 1e-10;
template <class T>
enable_if_t<is_integral_v<T>, int> sgn(T a) { return (a > 0) - (a < 0); }
template <class T>
enable_if_t<is_floating_point_v<T>, int> sgn(double a) { return (a > EPS) - (a < -EPS); }
template <class T>
struct vec2 {
    T x, y;
    vec2(){};
    vec2(T _x, T _y) : x(_x), y(_y) {}
    vec2 operator+() const { return *this; }
    vec2 operator-() const { return {-x, -y}; }
    vec2 operator+(const vec2& v) const { return {x + v.x, y + v.y}; }
    vec2 operator-(const vec2& v) const { return {x - v.x, y - v.y}; }
    vec2 operator*(T s) const { return {x * s, y * s}; }
    vec2 operator/(T s) const {
        assert(s != 0);
        return {x / s, y / s};
    }
    vec2& operator+=(const vec2& v) {
        x += v.x, y += v.y;
        return *this;
    }
    vec2& operator-=(const vec2& v) {
        x -= v.x, y -= v.y;
        return *this;
    }
    vec2& operator*=(T s) {
        x *= s, y *= s;
        return *this;
    }
    bool operator==(const vec2& v) { return sgn(x - v.x) == 0 && sgn(y - v.y) == 0; }
    bool operator!=(const vec2& v) { return !(*this == v); }
    bool operator<(const vec2& v) { return sgn(x - v.x) ? sgn(x - v.x) < 0 : sgn(y - v.y) < 0; }
    double len() const { return sqrt(len_sq()); }
    T len_sq() const { return x * x + y * y; }
    T dot(const vec2& v) const { return x * v.x + y * v.y; }
    T cross(const vec2& v) const { return x * v.y - y * v.x; }
    vec2 rotate(double arg) const {
        assert(is_floating_point_v<T>);
        double cs = cos(arg), sn = sin(arg);
        return {x * cs - y * sn, x * sn + y * cs};
    }
    double angle() const { return atan2(y, x); }
    vec2 normalized() const {
        assert(is_floating_point_v<T>);
        return *this / len();
    }
    vec2 normal_unitized() const {
        assert(is_floating_point_v<T>);
        return {-normalized().y, normalized().x};
    }
    friend vec2 operator*(T s, const vec2& v) { return {s * v.x, s * v.y}; }
    friend istream& operator>>(istream& is, vec2& v) { return is >> v.x >> v.y; }
    friend ostream& operator<<(ostream& os, const vec2& v) { return os << v.x << ' ' << v.y; }
};
template <class T>
int inter_seg_pt(const vec2<T>& a, const vec2<T>& b, const vec2<T>& c) {
    int f = sgn((b - a).cross(c - a));
    if (f) return f;
    if (sgn((b - a).dot(c - a)) < 0) return -2;
    if (sgn((a - b).dot(c - b)) < 0) return 2;
    return 0;
}
template <class T>
bool is_intersect_seg(const vec2<T>& a0, const vec2<T>& a1, const vec2<T>& b0, const vec2<T>& b1) {
    return inter_seg_pt(a0, a1, b0) * inter_seg_pt(a0, a1, b1) <= 0 && inter_seg_pt(b0, b1, a0) * inter_seg_pt(b0, b1, a1) <= 0;
}
template <int M>
struct static_modint {
    using mint = static_modint;
    static_modint(){}
    static_modint(long long v) {
        v %= 1ll * M;
        if (v < 0) v += M;
        _v = (uint)v;
    };
    constexpr mint operator-() { return mint() - *this; }
    mint operator+(const mint &r) { return mint(*this) += r; }
    mint operator-(const mint &r) { return mint(*this) -= r; }
    mint operator*(const mint &r) { return mint(*this) *= r; }
    mint operator/(const mint &r) { return mint(*this) /= r; }
    mint &operator+=(const mint &r) {
        _v += r._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint &operator-=(const mint &r) {
        _v -= r._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint &operator*=(const mint &r) {
        _v = uint(1ll * _v * r._v % umod());
        return *this;
    }
    mint &operator/=(const mint &r) { return *this = *this * r.inv(); }
    bool operator==(const mint &r) { return this->_v == r._v; }
    bool operator!=(const mint &r) { return this->_v != r._v; }
    constexpr uint val() const { return _v; }
    mint pow(int n) {
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x, n /= 2;
        }
        return r;
    }
    mint inv() { return pow(umod() - 2); }
    friend istream &operator>>(istream &is, mint &x) {
        long long v;
        is >> v;
        x = v;
        return is;
    }
    friend ostream &operator<<(ostream &os, const mint x) { return os << x.val(); }

   private:
    uint _v;
    static constexpr uint umod() { return M; }
};
using mint = static_modint<1000000007>;
mint modpow(mint x, int n) { return x.pow(n); }
int chromatic_num(vector<vector<bool>> &adja) {
    int n = adja.size(), neighbor[n];
    for (int u = 0; u < n; u++) {
        neighbor[u] = 1 << u;
        for (int v = 0; v < n; v++) {
            if (adja[u][v]) neighbor[u] |= 1 << v;
        }
    }
    int independent[1 << n];
    independent[0] = 1;
    for (int s = 1; s < 1 << n; s++) {
        int u = __builtin_ctz(s);
        independent[s] = independent[s & ~(1 << u)] + independent[s & ~neighbor[u]];
    }
    auto valid = [&](int mid) {
        mint g = 0;
        for (int s = 0; s < 1 << n; s++) {
            if ((n - __builtin_popcount(s)) & 1){
                g -= modpow(independent[s], mid);
            } else {
                g += modpow(independent[s], mid);
            }
        }
        return g != 0;
    };
    int ng = 0, ok = n;
    while (abs(ok - ng) > 1) {
        int mid = (ok + ng) / 2;
        (valid(mid) ? ok : ng) = mid;
    }
    return ok;
}
int main() {
    using vec = vec2<int>;
    while (true) {
        int n;
        cin >> n;
        if (!n) break;
        vector<vector<pair<vec, vec>>> lines(n);
        for (int u = 0; u < n; u++) {
            int num, x0 = -1, y0 = -1;
            cin >> num;
            for (int i = 0; i < num; i++) {
                int x, y;
                cin >> x >> y;
                if (i) lines[u].emplace_back(vec(x0, y0), vec(x, y));
                swap(x, x0), swap(y, y0);
            }
        }
        vector adja(n, vector<bool>(n));
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                bool connected = false;
                for (auto &[pi1, pi2] : lines[i]) {
                    for (auto& [pj1, pj2] : lines[j]) {
                        if (is_intersect_seg(pi1, pi2, pj1, pj2)) {
                            connected = true;
                            break;
                        }
                    }
                    if (connected) break;
                }
                if (connected) adja[i][j] = adja[j][i] = true;
            }
        }
        cout << chromatic_num(adja) << '\n';
    }
    return 0;
}
accuracy: 1
time_ms: 2570
memory_kb: 19608
final_rank: 7

code_snippet: #line 1 "main.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2136"

#include <cstdint>
#include <iostream>
#include <vector>
#line 1 "/home/anqooqie/.proconlib/tools/vector2.hpp"



#include <cmath>
#include <type_traits>
#include <cstddef>
#include <array>
#include <string>
#include <functional>
#include <limits>
#line 1 "/home/anqooqie/.proconlib/tools/pair_hash.hpp"



#line 5 "/home/anqooqie/.proconlib/tools/pair_hash.hpp"
#include <utility>
#include <random>
#line 9 "/home/anqooqie/.proconlib/tools/pair_hash.hpp"


#line 13 "/home/anqooqie/.proconlib/tools/vector2.hpp"

namespace tools {

  template <typename T>
  class vector2 {
  public:
    T x;
    T y;

    vector2() :
      vector2(T(), T()) {
    }

    vector2(const T& x, const T& y) :
      x(x),
      y(y) {
    }

    double norm() const {
      return ::std::sqrt(this->squaredNorm());
    }

    T squared_norm() const {
      return this->inner_product(*this);
    }

    template <typename SFINAE_T = T, typename ::std::enable_if<::std::is_same<SFINAE_T, double>::value, ::std::nullptr_t>::type = nullptr>
    ::tools::vector2<double> normalized() const {
      return *this / this->norm();
    }

    ::tools::vector2<T> operator+() const {
      return *this;
    }

    ::tools::vector2<T> operator-() const {
      return ::tools::vector2<T>(-this->x, -this->y);
    }

    friend ::tools::vector2<T> operator+(const ::tools::vector2<T>& lhs, const ::tools::vector2<T>& rhs) {
      return ::tools::vector2<T>(lhs.x + rhs.x, lhs.y + rhs.y);
    }

    friend ::tools::vector2<T> operator-(const ::tools::vector2<T>& lhs, const ::tools::vector2<T>& rhs) {
      return ::tools::vector2<T>(lhs.x - rhs.x, lhs.y - rhs.y);
    }

    template <typename OTHER, typename ::std::enable_if<!::std::is_same<OTHER, ::tools::vector2<T>>::value, ::std::nullptr_t>::type = nullptr>
    friend ::tools::vector2<T> operator*(const ::tools::vector2<T>& lhs, const OTHER& rhs) {
      return ::tools::vector2<T>(lhs.x * rhs, lhs.y * rhs);
    }
    template <typename OTHER, typename ::std::enable_if<!::std::is_same<OTHER, ::tools::vector2<T>>::value, ::std::nullptr_t>::type = nullptr>
    friend ::tools::vector2<T> operator*(const OTHER& lhs, const ::tools::vector2<T>& rhs) {
      return ::tools::vector2<T>(lhs * rhs.x, lhs * rhs.y);
    }

    template <typename OTHER, typename ::std::enable_if<!::std::is_same<OTHER, ::tools::vector2<T>>::value, ::std::nullptr_t>::type = nullptr>
    friend ::tools::vector2<T> operator/(const ::tools::vector2<T>& lhs, const OTHER& rhs) {
      return ::tools::vector2<T>(lhs.x / rhs, lhs.y / rhs);
    }

    T inner_product(const ::tools::vector2<T>& other) const {
      return this->x * other.x + this->y * other.y;
    }

    T outer_product(const ::tools::vector2<T>& other) const {
      return this->x * other.y - this->y * other.x;
    }

    ::tools::vector2<T>& operator+=(const ::tools::vector2<T>& other) {
      return *this = *this + other;
    }

    ::tools::vector2<T>& operator-=(const ::tools::vector2<T>& other) {
      return *this = *this - other;
    }

    template <typename OTHER, typename ::std::enable_if<!::std::is_same<OTHER, ::tools::vector2<T>>::value, ::std::nullptr_t>::type = nullptr>
    ::tools::vector2<T>& operator*=(const OTHER& other) {
      return *this = *this * other;
    }

    template <typename OTHER, typename ::std::enable_if<!::std::is_same<OTHER, ::tools::vector2<T>>::value, ::std::nullptr_t>::type = nullptr>
    ::tools::vector2<T>& operator/=(const OTHER& other) {
      return *this = *this / other;
    }

    friend bool operator==(const ::tools::vector2<T>& lhs, const ::tools::vector2<T>& rhs) {
      return lhs.x == rhs.x && lhs.y == rhs.y;
    }

    friend bool operator!=(const ::tools::vector2<T>& lhs, const ::tools::vector2<T>& rhs) {
      return lhs.x != rhs.x || lhs.y != rhs.y;
    }

    static ::std::array<::tools::vector2<T>, 4> four_directions() {
      return ::std::array<::tools::vector2<T>, 4>({
        ::tools::vector2<T>(static_cast<T>(-1), static_cast<T>(0)),
        ::tools::vector2<T>(static_cast<T>(1), static_cast<T>(0)),
        ::tools::vector2<T>(static_cast<T>(0), static_cast<T>(-1)),
        ::tools::vector2<T>(static_cast<T>(0), static_cast<T>(1))
      });
    }

    static ::std::array<::tools::vector2<T>, 8> eight_directions() {
      return ::std::array<::tools::vector2<T>, 8>({
        ::tools::vector2<T>(static_cast<T>(-1), static_cast<T>(-1)),
        ::tools::vector2<T>(static_cast<T>(-1), static_cast<T>(0)),
        ::tools::vector2<T>(static_cast<T>(-1), static_cast<T>(1)),
        ::tools::vector2<T>(static_cast<T>(0), static_cast<T>(-1)),
        ::tools::vector2<T>(static_cast<T>(0), static_cast<T>(1)),
        ::tools::vector2<T>(static_cast<T>(0), static_cast<T>(-1)),
        ::tools::vector2<T>(static_cast<T>(1), static_cast<T>(-1)),
        ::tools::vector2<T>(static_cast<T>(1), static_cast<T>(0)),
        ::tools::vector2<T>(static_cast<T>(1), static_cast<T>(1))
      });
    }
  };
}

#line 1 "/home/anqooqie/.proconlib/tools/ccw.hpp"



#line 5 "/home/anqooqie/.proconlib/tools/ccw.hpp"

namespace tools {
  template <typename T>
  ::std::int_fast64_t ccw(const ::tools::vector2<T>& a,  ::tools::vector2<T> b, ::tools::vector2<T> c) {
    b -= a;
    c -= a;
    if (b.outer_product(c) > 0) return +1;
    if (b.outer_product(c) < 0) return -1;
    if (b.inner_product(c) < 0) return +2;
    if (b.squared_norm() < c.squared_norm()) return -2;
    return 0;
  }
}


#line 1 "/home/anqooqie/.proconlib/tools/chromatic_number.hpp"



#line 7 "/home/anqooqie/.proconlib/tools/chromatic_number.hpp"
#include <cassert>
#line 1 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/modint.hpp"



#line 5 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/modint.hpp"
#include <numeric>
#line 7 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/modint.hpp"

#ifdef _MSC_VER
#include <intrin.h>
#endif

#line 1 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/internal_math.hpp"



#line 5 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/internal_math.hpp"

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

// @param m `1 <= m`
// @return x mod m
constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

// Fast modular multiplication by barrett reduction
// Reference: https://en.wikipedia.org/wiki/Barrett_reduction
// NOTE: reconsider after Ice Lake
struct barrett {
    unsigned int _m;
    unsigned long long im;

    // @param m `1 <= m < 2^31`
    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    // @return m
    unsigned int umod() const { return _m; }

    // @param a `0 <= a < m`
    // @param b `0 <= b < m`
    // @return `a * b % m`
    unsigned int mul(unsigned int a, unsigned int b) const {
        // [1] m = 1
        // a = b = im = 0, so okay

        // [2] m >= 2
        // im = ceil(2^64 / m)
        // -> im * m = 2^64 + r (0 <= r < m)
        // let z = a*b = c*m + d (0 <= c, d < m)
        // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
        // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
        // ((ab * im) >> 64) == c or c + 1
        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

// @param n `0 <= n`
// @param m `1 <= m`
// @return `(x ** n) % m`
constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

// Reference:
// M. Forisek and J. Jancina,
// Fast Primality Testing for Integers That Fit into a Machine Word
// @param n `0 <= n`
constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

// @param b `1 <= b`
// @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g
constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    // Contracts:
    // [1] s - m0 * a = 0 (mod b)
    // [2] t - m1 * a = 0 (mod b)
    // [3] s * |m1| + t * |m0| <= b
    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b

        // [3]:
        // (s - t * u) * |m1| + t * |m0 - m1 * u|
        // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u)
        // = s * |m1| + t * |m0| <= b

        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    // by [3]: |m0| <= b/g
    // by g != b: |m0| < b/g
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

// Compile time primitive root
// @param m must be prime
// @return primitive root (and minimum in now)
constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

}  // namespace internal

}  // namespace atcoder


#line 1 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/internal_type_traits.hpp"



#line 7 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/internal_type_traits.hpp"

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder


#line 14 "/home/anqooqie/.proconlib/lib/ac-library/atcoder/modint.hpp"

namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder


#line 1 "/home/anqooqie/.proconlib/tools/ntz.hpp"



#line 1 "/home/anqooqie/.proconlib/tools/popcount.hpp"



#line 5 "/home/anqooqie/.proconlib/tools/popcount.hpp"

namespace tools {

  inline ::std::uint32_t popcount(::std::uint32_t x) {
    x = (x & static_cast<::std::uint32_t>(0x55555555ull)) + (x >> static_cast<::std::uint32_t>(1) & static_cast<::std::uint32_t>(0x55555555ull));
    x = (x & static_cast<::std::uint32_t>(0x33333333ull)) + (x >> static_cast<::std::uint32_t>(2) & static_cast<::std::uint32_t>(0x33333333ull));
    x = (x & static_cast<::std::uint32_t>(0x0f0f0f0full)) + (x >> static_cast<::std::uint32_t>(4) & static_cast<::std::uint32_t>(0x0f0f0f0full));
    x = (x & static_cast<::std::uint32_t>(0x00ff00ffull)) + (x >> static_cast<::std::uint32_t>(8) & static_cast<::std::uint32_t>(0x00ff00ffull));
    return (x & static_cast<::std::uint32_t>(0x0000ffffull)) + (x >> static_cast<::std::uint32_t>(16) & static_cast<::std::uint32_t>(0x0000ffffull));
  }

  inline ::std::uint64_t popcount(::std::uint64_t x) {
    x = (x & static_cast<::std::uint64_t>(0x5555555555555555ull)) + (x >> static_cast<::std::uint64_t>(1) & static_cast<::std::uint64_t>(0x5555555555555555ull));
    x = (x & static_cast<::std::uint64_t>(0x3333333333333333ull)) + (x >> static_cast<::std::uint64_t>(2) & static_cast<::std::uint64_t>(0x3333333333333333ull));
    x = (x & static_cast<::std::uint64_t>(0x0f0f0f0f0f0f0f0full)) + (x >> static_cast<::std::uint64_t>(4) & static_cast<::std::uint64_t>(0x0f0f0f0f0f0f0f0full));
    x = (x & static_cast<::std::uint64_t>(0x00ff00ff00ff00ffull)) + (x >> static_cast<::std::uint64_t>(8) & static_cast<::std::uint64_t>(0x00ff00ff00ff00ffull));
    x = (x & static_cast<::std::uint64_t>(0x0000ffff0000ffffull)) + (x >> static_cast<::std::uint64_t>(16) & static_cast<::std::uint64_t>(0x0000ffff0000ffffull));
    return (x & static_cast<::std::uint64_t>(0x00000000ffffffffull)) + (x >> static_cast<::std::uint64_t>(32) & static_cast<::std::uint64_t>(0x00000000ffffffffull));
  }

  inline ::std::int32_t popcount(::std::int32_t x) {
    return static_cast<::std::int32_t>(::tools::popcount(static_cast<::std::uint32_t>(x)));
  }

  inline ::std::int64_t popcount(::std::int64_t x) {
    return static_cast<::std::int64_t>(::tools::popcount(static_cast<::std::uint64_t>(x)));
  }
}


#line 6 "/home/anqooqie/.proconlib/tools/ntz.hpp"

namespace tools {

  inline ::std::uint32_t ntz(const ::std::uint32_t& x) {
    return ::tools::popcount((x & -x) - static_cast<::std::uint32_t>(1));
  }

  inline ::std::uint64_t ntz(const ::std::uint64_t& x) {
    return ::tools::popcount((x & -x) - static_cast<::std::uint64_t>(1));
  }

  inline ::std::int32_t ntz(::std::int32_t x) {
    return static_cast<::std::int32_t>(::tools::ntz(static_cast<::std::uint32_t>(x)));
  }

  inline ::std::int64_t ntz(::std::int64_t x) {
    return static_cast<::std::int64_t>(::tools::ntz(static_cast<::std::uint64_t>(x)));
  }
}


#line 11 "/home/anqooqie/.proconlib/tools/chromatic_number.hpp"

// Source: https://drken1215.hatenablog.com/entry/2019/01/16/030000
// License: unknown
// Author: drken

namespace tools {
  class chromatic_number {
  private:
    ::std::vector<::std::uint_fast64_t> neighbor;

  public:
    chromatic_number() = default;
    chromatic_number(const ::tools::chromatic_number&) = default;
    chromatic_number(::tools::chromatic_number&&) = default;
    ~chromatic_number() = default;
    ::tools::chromatic_number& operator=(const ::tools::chromatic_number&) = default;
    ::tools::chromatic_number& operator=(::tools::chromatic_number&&) = default;

    explicit chromatic_number(const ::std::size_t n) : neighbor(n) {
      for (::std::size_t i = 0; i < n; ++i) {
        this->neighbor[i] = (::std::uint_fast64_t(1) << ::std::uint_fast64_t(i));
      }
    }

    ::std::size_t node_count() const {
      return this->neighbor.size();
    }

    void add_edge(const ::std::size_t s, const ::std::size_t t) {
      assert(s < this->node_count());
      assert(t < this->node_count());
      this->neighbor[s] |= (::std::uint_fast64_t(1) << ::std::uint_fast64_t(t));
    }

    ::std::int_fast64_t query() const {
      const auto pow2 = [](const ::std::uint_fast64_t x) {
        return ::std::uint_fast64_t(1) << x;
      };
      const auto& set = pow2;

      // I[S] := #. of indepndent subsets of S
      ::std::vector<::atcoder::modint1000000007> I(pow2(this->node_count()));
      I[0] = ::atcoder::modint1000000007(1);
      for (::std::uint_fast64_t S = 1; S < pow2(this->node_count()); ++S) {
        const ::std::uint_fast64_t v = ::tools::ntz(S);
        I[S] = I[S & ~set(v)] + I[S & ~this->neighbor[v]];
      }

      ::std::int_fast64_t ng = 0;
      ::std::int_fast64_t ok = this->node_count();
      while (ok - ng > 1) {
        ::std::int_fast64_t k = (ok + ng) / 2;

        // g[S] := #. of "k independent sets" which cover S just
        // f[S] := #. of "k independent sets" which consist of subsets of S
        // then
        //   f[S] = sum_{T in S} g(T)
        //   g[S] = sum_{T in S} (-1)^(|S|-|T|)f[T]
        ::atcoder::modint1000000007 g(0);
        for (::std::uint_fast64_t S = 0; S < pow2(this->node_count()); ++S) {
          if ((::std::uint_fast64_t(this->node_count()) - ::tools::popcount(S)) & 1) {
            g -= I[S].pow(k);
          } else {
            g += I[S].pow(k);
          }
        }

        if (g.val() != 0) {
          ok = k;
        } else {
          ng = k;
        }
      }

      return ok;
    }
  };
}


#line 9 "main.cpp"

using i64 = std::int_fast64_t;

int main() {
  std::cin.tie(nullptr);
  std::ios_base::sync_with_stdio(false);

  for (i64 N; std::cin >> N, N > 0;) {
    std::vector<std::vector<tools::vector2<i64>>> lines(N);
    for (i64 i = 0; i < N; ++i) {
      i64 S;
      std::cin >> S;
      for (i64 j = 0; j < S; ++j) {
        i64 X, Y;
        std::cin >> X >> Y;
        lines[i].emplace_back(X, Y);
      }
    }

    tools::chromatic_number graph(N);
    for (i64 i = 0; i < N; ++i) {
      for (i64 j = i + 1; j < N; ++j) {
        if ([&]() {
          for (i64 k = 1; k < i64(lines[i].size()); ++k) {
            for (i64 l = 1; l < i64(lines[j].size()); ++l) {
              if (tools::ccw(lines[i][k - 1], lines[i][k], lines[j][l - 1]) * tools::ccw(lines[i][k - 1], lines[i][k], lines[j][l]) <= 0
                  && tools::ccw(lines[j][l - 1], lines[j][l], lines[i][k - 1]) * tools::ccw(lines[j][l - 1], lines[j][l], lines[i][k]) <= 0) {
                return true;
              }
            }
          }
          return false;
        }()) {
          graph.add_edge(i, j);
        }
      }
    }

    std::cout << graph.query() << '\n';
  }
  return 0;
}
accuracy: 1
time_ms: 2350
memory_kb: 19776
final_rank: 2

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <queue>
#include <string>
#include <map>
#include <set>
#include <stack>
#include <tuple>
#include <deque>
#include <array>
#include <numeric>
#include <bitset>
#include <iomanip>
#include <cassert>
#include <chrono>
#include <random>
#include <limits>
#include <iterator>
#include <functional>
#include <sstream>
#include <fstream>
#include <complex>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
using namespace std;

using ll = long long;
constexpr int INF = 1001001001;
constexpr int mod = 1000000007;
// constexpr int mod = 998244353;

template<class T>
inline bool chmax(T& x, T y){
    if(x < y){
        x = y;
        return true;
    }
    return false;
}
template<class T>
inline bool chmin(T& x, T y){
    if(x > y){
        x = y;
        return true;
    }
    return false;
}

using ld = long double;
using Point = complex<ld>;
using Set = vector<Point>;
const ld pi = acos(-1);
constexpr ld eps = 1e-9;
constexpr ld inf = 1e12;

// 外積
ld cross(const Point& a, const Point& b){
    return a.real() * b.imag() - a.imag() * b.real();
}

// 内積
ld dot(const Point& a, const Point& b){
    return a.real() * b.real() + a.imag() * b.imag();
}

// 象限を返す (推移率を成立させるために、原点は 0 象限とする)
int get_orthant(const Point& p){
    if(abs(p.real()) < eps && abs(p.imag()) < eps)  return 0;
    if(p.imag() > 0)    return p.real() > 0 ? 1 : 2;
    return p.real() > 0 ? 4 : 3;
}

// 偏角ソート用比較関数 (arg や atan2 での誤差が怖い場合に用いる)
// 第 1 象限から第 4 象限にかけて、偏角の小さい順にソートする。
bool arg_comp(const Point& p1, const Point& p2){
    int ort1 = get_orthant(p1), ort2 = get_orthant(p2);
    if(ort1 != ort2)    return ort1 < ort2;
    return cross(p1, p2) > 0;
}

enum CCW_RESULT{
    CCW = +1, CW = -1, BEHIND = +2, FRONT = -2, ON = 0
};
// b, c に対して、a がどの位置にあるか
int ccw(Point a, Point b, Point c){
    b -= a, c -= a;
    if(cross(b, c) > eps)   return CCW;     // 上側
    if(cross(b, c) < -eps)  return CW;      // 下側
    
    // cross(b, c) == 0
    if(dot(b, c) < 0)   return BEHIND;      // 同一直線上の左側
    if(norm(b) < norm(c))   return FRONT;   // 同一直線上の右側
    return ON;      // 線分 ab 上にある
}

namespace std{
bool operator<(const Point& a, const Point &b){
    return abs(real(a) - real(b)) > eps ? real(a) < real(b) : imag(a) < imag(b);
}
}

// 直線 (2つの点で表す)
struct Line : public vector<Point>{
    Line(const Point& a = Point(), const Point& b = Point()) : vector<Point>(2){
        begin()[0] = a;
        begin()[1] = b;
    }

    // Ax + By + C = 0
    Line(ld A, ld B, ld C){
        if(abs(A) < eps && abs(B) < eps){
            abort();
        }
        else if(abs(A) < eps){
            *this = Line(Point(0, -C / B), Point(1, -C / B));
        }
        else if(abs(B) < eps){
            *this = Line(Point(-C / A, 0), Point(-C / A, 1));
        }
        else{
            *this = Line(Point(0, -C / B), Point(-C / A, 0));
        }
    }
};

/* 交差判定 */
// 直線と直線
bool intersectLL(const Line& l, const Line& m){
    return abs(cross(l[1] - l[0], m[1] - m[0])) > eps ||    // 平行でない
           abs(cross(l[1] - l[0], m[0] - l[0])) < eps;      // 同じ直線
}
// 直線と線分
bool intersectLS(const Line &l, const Line& s){
    // cross(l[1] - l[0], s[0] - l[0]) : s[0] が左側
    // cross(l[1] - l[0], s[1] - l[0]) : s[1] が右側
    return cross(l[1] - l[0], s[0] - l[0]) * cross(l[1] - l[0], s[1] - l[0]) < eps;
}
// 直線と点
bool intersectLP(const Line& l, const Point& p){
    return abs(cross(l[1] - p, l[0] - p)) < eps;
}
// 線分と線分
bool intersectSS(const Line& s, const Line& t){
    return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 &&
           ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}
// 線分と点
bool intersectSP(const Line& s, const Point& p){
    // 三角不等式
    return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < eps;
}

int chromatic_number(const vector<vector<int>>& g){
    int N = g.size();
    vector<int> neighbor(N);
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < N; ++j){
            neighbor[i] |= g[i][j] << j;
        }
    }

    // I[S] := the number of Independent subset of S
    vector<int> I(1 << N);
    I[0] = 1;
    for(int S = 1; S < 1 << N; ++S){
        // LSB (Least Significant Bit)
        int v = __builtin_ctz(S);
        I[S] = I[S ^ (1 << v)] + I[(S ^ (1 << v)) & ~neighbor[v]];
    }

    constexpr int p = 1000000007;
    auto modpow = [&](int x, int n){
        int res = 1;
        while(n > 0){
            if(n & 1)   res = 1LL * res * x % p;
            x = 1LL * x * x % p;
            n >>= 1;
        }
        return res;
    };

    int low = 0, high = N;
    while(high - low > 1){
        int X = (low + high) / 2;
        
        int way = 0;
        for(int S = 0; S < 1 << N; ++S){
            if((N - __builtin_popcount(S)) & 1){
                if((way -= modpow(I[S], X)) < 0)  way += p;
            }
            else{
                if((way += modpow(I[S], X)) >= p) way -= p;
            }
        }

        if(way > 0) high = X;
        else    low = X;
    }

    return high;
}

void solve(int N){
    vector<vector<Line>> lines(N);
    for(int i = 0; i < N; ++i){
        int S, x, y;
        cin >> S >> x >> y;
        for(int j = 1; j < S; ++j){
            int nx, ny;
            cin >> nx >> ny;
            lines[i].emplace_back(Line(Point(x, y), Point(nx, ny)));
            x = nx, y = ny;
        }
    }
    vector<vector<int>> g(N, vector<int>(N));
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < i; ++j){
            for(auto& li : lines[i]){
                for(auto& lj : lines[j]){
                    if(intersectSS(li, lj)) g[i][j] = g[j][i] = 1;
                }
            }
        }
    }
    cout << chromatic_number(g) << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    while(cin >> N, N)  solve(N);

    return 0;
}
accuracy: 1
time_ms: 2680
memory_kb: 19652
final_rank: 8

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <queue>
#include <string>
#include <map>
#include <set>
#include <stack>
#include <tuple>
#include <deque>
#include <array>
#include <numeric>
#include <bitset>
#include <iomanip>
#include <cassert>
#include <chrono>
#include <random>
#include <limits>
#include <iterator>
#include <functional>
#include <sstream>
#include <fstream>
#include <complex>
#include <cstring>
#include <unordered_map>
#include <unordered_set>
using namespace std;

using ll = long long;
constexpr int INF = 1001001001;
constexpr int mod = 1000000007;
// constexpr int mod = 998244353;

template<class T>
inline bool chmax(T& x, T y){
    if(x < y){
        x = y;
        return true;
    }
    return false;
}
template<class T>
inline bool chmin(T& x, T y){
    if(x > y){
        x = y;
        return true;
    }
    return false;
}

using ld = long double;
using Point = complex<ld>;
using Set = vector<Point>;
const ld pi = acos(-1);
constexpr ld eps = 1e-9;
constexpr ld inf = 1e12;

// 外積
ld cross(const Point& a, const Point& b){
    return a.real() * b.imag() - a.imag() * b.real();
}

// 内積
ld dot(const Point& a, const Point& b){
    return a.real() * b.real() + a.imag() * b.imag();
}

// 象限を返す (推移率を成立させるために、原点は 0 象限とする)
int get_orthant(const Point& p){
    if(abs(p.real()) < eps && abs(p.imag()) < eps)  return 0;
    if(p.imag() > 0)    return p.real() > 0 ? 1 : 2;
    return p.real() > 0 ? 4 : 3;
}

// 偏角ソート用比較関数 (arg や atan2 での誤差が怖い場合に用いる)
// 第 1 象限から第 4 象限にかけて、偏角の小さい順にソートする。
bool arg_comp(const Point& p1, const Point& p2){
    int ort1 = get_orthant(p1), ort2 = get_orthant(p2);
    if(ort1 != ort2)    return ort1 < ort2;
    return cross(p1, p2) > 0;
}

enum CCW_RESULT{
    CCW = +1, CW = -1, BEHIND = +2, FRONT = -2, ON = 0
};
// b, c に対して、a がどの位置にあるか
int ccw(Point a, Point b, Point c){
    b -= a, c -= a;
    if(cross(b, c) > eps)   return CCW;     // 上側
    if(cross(b, c) < -eps)  return CW;      // 下側
    
    // cross(b, c) == 0
    if(dot(b, c) < 0)   return BEHIND;      // 同一直線上の左側
    if(norm(b) < norm(c))   return FRONT;   // 同一直線上の右側
    return ON;      // 線分 ab 上にある
}

namespace std{
bool operator<(const Point& a, const Point &b){
    return abs(real(a) - real(b)) > eps ? real(a) < real(b) : imag(a) < imag(b);
}
}

// 直線 (2つの点で表す)
struct Line : public vector<Point>{
    Line(const Point& a = Point(), const Point& b = Point()) : vector<Point>(2){
        begin()[0] = a;
        begin()[1] = b;
    }

    // Ax + By + C = 0
    Line(ld A, ld B, ld C){
        if(abs(A) < eps && abs(B) < eps){
            abort();
        }
        else if(abs(A) < eps){
            *this = Line(Point(0, -C / B), Point(1, -C / B));
        }
        else if(abs(B) < eps){
            *this = Line(Point(-C / A, 0), Point(-C / A, 1));
        }
        else{
            *this = Line(Point(0, -C / B), Point(-C / A, 0));
        }
    }
};

/* 交差判定 */
// 直線と直線
bool intersectLL(const Line& l, const Line& m){
    return abs(cross(l[1] - l[0], m[1] - m[0])) > eps ||    // 平行でない
           abs(cross(l[1] - l[0], m[0] - l[0])) < eps;      // 同じ直線
}
// 直線と線分
bool intersectLS(const Line &l, const Line& s){
    // cross(l[1] - l[0], s[0] - l[0]) : s[0] が左側
    // cross(l[1] - l[0], s[1] - l[0]) : s[1] が右側
    return cross(l[1] - l[0], s[0] - l[0]) * cross(l[1] - l[0], s[1] - l[0]) < eps;
}
// 直線と点
bool intersectLP(const Line& l, const Point& p){
    return abs(cross(l[1] - p, l[0] - p)) < eps;
}
// 線分と線分
bool intersectSS(const Line& s, const Line& t){
    return ccw(s[0], s[1], t[0]) * ccw(s[0], s[1], t[1]) <= 0 &&
           ccw(t[0], t[1], s[0]) * ccw(t[0], t[1], s[1]) <= 0;
}
// 線分と点
bool intersectSP(const Line& s, const Point& p){
    // 三角不等式
    return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < eps;
}

int chromatic_number(const vector<vector<int>>& g){
    int N = g.size();
    vector<int> neighbor(N);
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < N; ++j){
            neighbor[i] |= g[i][j] << j;
        }
    }

    // I[S] := the number of Independent subset of S
    vector<int> I(1 << N);
    I[0] = 1;
    for(int S = 1; S < 1 << N; ++S){
        // LSB (Least Significant Bit)
        int v = __builtin_ctz(S);
        I[S] = I[S ^ (1 << v)] + I[(S ^ (1 << v)) & ~neighbor[v]];
    }

    constexpr int p = 1000000007;
    auto modpow = [&](int x, int n){
        int res = 1;
        while(n > 0){
            if(n & 1)   res = 1LL * res * x % p;
            x = 1LL * x * x % p;
            n >>= 1;
        }
        return res;
    };

    int low = 0, high = N;
    while(high - low > 1){
        int X = (low + high) / 2;
        
        int way = 0;
        for(int S = 0; S < 1 << N; ++S){
            if((N - __builtin_popcount(S)) & 1){
                (way += p - modpow(I[S], X)) %= p;
            }
            else{
                (way += modpow(I[S], X)) %= p;
            }
        }

        if(way > 0) high = X;
        else    low = X;
    }

    return high;
}

void solve(int N){
    vector<vector<Line>> lines(N);
    for(int i = 0; i < N; ++i){
        int S, x, y;
        cin >> S >> x >> y;
        for(int j = 1; j < S; ++j){
            int nx, ny;
            cin >> nx >> ny;
            lines[i].emplace_back(Line(Point(x, y), Point(nx, ny)));
            x = nx, y = ny;
        }
    }
    vector<vector<int>> g(N, vector<int>(N));
    for(int i = 0; i < N; ++i){
        for(int j = 0; j < i; ++j){
            for(auto& li : lines[i]){
                for(auto& lj : lines[j]){
                    if(intersectSS(li, lj)) g[i][j] = g[j][i] = 1;
                }
            }
        }
    }
    cout << chromatic_number(g) << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N;
    while(cin >> N, N)  solve(N);

    return 0;
}
accuracy: 1
time_ms: 2880
memory_kb: 19760
final_rank: 10

code_snippet: #define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
using ll = long long;
constexpr int INF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr double EPS = 1e-8;
constexpr int MOD = 1000000007;
// constexpr int MOD = 998244353;
constexpr int dy[] = {1, 0, -1, 0}, dx[] = {0, -1, 0, 1};
constexpr int dy8[] = {1, 1, 0, -1, -1, -1, 0, 1}, dx8[] = {0, -1, -1, -1, 0, 1, 1, 1};
template <typename T, typename U> inline bool chmax(T &a, U b) { return a < b ? (a = b, true) : false; }
template <typename T, typename U> inline bool chmin(T &a, U b) { return a > b ? (a = b, true) : false; }
struct IOSetup {
  IOSetup() {
    std::cin.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    std::cout << fixed << setprecision(20);
  }
} iosetup;

namespace geometry {
using Real = double;
constexpr long double PI = 3.14159265358979323846;

int sgn(Real x) {
  constexpr Real EPS = 1e-8;
  return x > EPS ? 1 : x < -EPS ? -1 : 0;
}

Real degree_to_radian(Real d) { return d * PI / 180; }
Real radian_to_degree(Real r) { return r * 180 / PI; }

struct Point {
  Real x, y;
  Point(Real x = 0, Real y = 0) : x(x), y(y) {}
  Real abs() const { return std::sqrt(norm()); }
  Real arg() const { Real res = std::atan2(y, x); return res < 0 ? res + PI * 2 : res; }
  Real norm() const { return x * x + y * y; }
  Point rotate(Real angle) const { Real cs = std::cos(angle), sn = std::sin(angle); return Point(x * cs - y * sn, x * sn + y * cs); }
  Point unit_vector() const { Real a = abs(); return Point(x / a, y / a); }
  std::pair<Point, Point> normal_unit_vector() const { Point p = unit_vector(); return {Point(-p.y, p.x), Point(p.y, -p.x)}; }
  Point &operator+=(const Point &p) { x += p.x; y += p.y; return *this; }
  Point &operator-=(const Point &p) { x -= p.x; y -= p.y; return *this; }
  Point &operator*=(Real k) { x *= k; y *= k; return *this; }
  Point &operator/=(Real k) { x /= k; y /= k; return *this; }
  bool operator<(const Point &p) const { int x_sgn = sgn(p.x - x); return x_sgn != 0 ? x_sgn == 1 : sgn(p.y - y) == 1; }
  bool operator<=(const Point &p) const { return !(p < *this); }
  bool operator>(const Point &p) const { return p < *this; }
  bool operator>=(const Point &p) const { return !(*this < p); }
  Point operator+() const { return *this; }
  Point operator-() const { return Point(-x, -y); }
  Point operator+(const Point &p) const { return Point(*this) += p; }
  Point operator-(const Point &p) const { return Point(*this) -= p; }
  Point operator*(Real k) const { return Point(*this) *= k; }
  Point operator/(Real k) const { return Point(*this) /= k; }
  friend std::ostream &operator<<(std::ostream &os, const Point &p) { return os << '(' << p.x << ", " << p.y << ')'; }
  friend std::istream &operator>>(std::istream &is, Point &p) { Real x, y; is >> x >> y; p = Point(x, y); return is; }
};

struct Segment {
  Point s, t;
  Segment(const Point &s = {0, 0}, const Point &t = {0, 0}) : s(s), t(t) {}
};
struct Line : Segment {
  using Segment::Segment;
  Line(Real a, Real b, Real c) {
    if (sgn(a) == 0) {
      s = Point(0, -c / b); t = Point(1, s.y);
    } else if (sgn(b) == 0) {
      s = Point(-c / a, 0); t = Point(s.x, 1);
    } else if (sgn(c) == 0) {
      s = Point(0, 0); t = Point(1, -a / b);
    } else {
      s = Point(0, -c / b); t = Point(-c / a, 0);
    }
  }
};

struct Circle {
  Point p; Real r;
  Circle(const Point &p = {0, 0}, Real r = 0) : p(p), r(r) {}
};

Real cross(const Point &a, const Point &b) { return a.x * b.y - a.y * b.x; }
Real dot(const Point &a, const Point &b) { return a.x * b.x + a.y * b.y; }

int ccw(const Point &a, const Point &b, const Point &c) {
  Point ab = b - a, ac = c - a;
  int sign = sgn(cross(ab, ac));
  if (sign == 0) {
    if (sgn(dot(ab, ac)) == -1) return 2;
    if (sgn(ac.norm() - ab.norm()) == 1) return -2;
  }
  return sign;
}

Real get_angle(const Point &a, const Point &b, const Point &c) {
  Real ba_arg = (a - b).arg(), bc_arg = (c - b).arg();
  if (ba_arg > bc_arg) std::swap(ba_arg, bc_arg);
  return std::min(bc_arg - ba_arg, static_cast<Real>(PI * 2 - (bc_arg - ba_arg)));
}

Real closest_pair(std::vector<Point> ps) {
  int n = ps.size();
  assert(n > 1);
  std::sort(ps.begin(), ps.end());
  std::function<Real(int, int)> rec = [&ps, &rec](int left, int right) -> Real {
    int mid = (left + right) >> 1;
    Real x_mid = ps[mid].x, d = std::numeric_limits<Real>::max();
    if (left + 1 < mid) {
      Real tmp = rec(left, mid);
      if (tmp < d) d = tmp;
    }
    if (mid + 1 < right) {
      Real tmp = rec(mid, right);
      if (tmp < d) d = tmp;
    }
    std::inplace_merge(ps.begin() + left, ps.begin() + mid, ps.begin() + right, [&](const Point &a, const Point &b) -> bool { return sgn(b.y - a.y) == 1; });
    std::vector<Point> tmp;
    for (int i = left; i < right; ++i) {
      if (sgn(std::abs(ps[i].x - x_mid) - d) == 1) continue;
      for (int j = static_cast<int>(tmp.size()) - 1; j >= 0; --j) {
        Point now = ps[i] - tmp[j];
        if (sgn(now.y - d) == 1) break;
        Real tmp = now.abs();
        if (tmp < d) d = tmp;
      }
      tmp.emplace_back(ps[i]);
    }
    return d;
  };
  return rec(0, n);
}

Point projection(const Segment &a, const Point &b) { return a.s + (a.t - a.s) * dot(a.t - a.s, b - a.s) / (a.t - a.s).norm(); }
Point reflection(const Segment &a, const Point &b) { return projection(a, b) * 2 - b; }

bool is_parallel(const Segment &a, const Segment &b) { return sgn(cross(a.t - a.s, b.t - b.s)) == 0; }
bool is_orthogonal(const Segment &a, const Segment &b) { return sgn(dot(a.t - a.s, b.t - b.s)) == 0; }

Real distance(const Point&, const Point&);
Real distance(const Segment&, const Point&);
Real distance(const Line&, const Point&);
int sizeof_common_tangent(const Circle&, const Circle&);
bool has_intersected(const Segment &a, const Point &b) { return ccw(a.s, a.t, b) == 0; }
bool has_intersected(const Segment &a, const Segment &b) { return ccw(a.s, a.t, b.s) * ccw(a.s, a.t, b.t) <= 0 && ccw(b.s, b.t, a.s) * ccw(b.s, b.t, a.t) <= 0; }
bool has_intersected(const Line &a, const Point &b) { int c = ccw(a.s, a.t, b); return c != 1 && c != -1; }
bool has_intersected(const Line &a, const Segment &b) { return ccw(a.s, a.t, b.s) * ccw(a.s, a.t, b.t) != 1; }
bool has_intersected(const Line &a, const Line &b) { return sgn(cross(a.t - a.s, b.t - b.s)) != 0 || sgn(cross(a.t - a.s, b.s - a.s)) == 0; }
bool has_intersected(const Circle &a, const Point &b) { return sgn(distance(a.p, b) - a.r) == 0; }
bool has_intersected(const Circle &a, const Segment &b) { return sgn(a.r - distance(b, a.p)) != -1 && sgn(std::max(distance(a.p, b.s), distance(a.p, b.t)) - a.r) != -1; }
bool has_intersected(const Circle &a, const Line &b) { return sgn(a.r - distance(b, a.p)) != -1; }
bool has_intersected(const Circle &a, const Circle &b) { return sizeof_common_tangent(a, b) > 0; }

Point intersection(const Line &a, const Line &b) {
  assert(has_intersected(a, b) && !is_parallel(a, b));
  return a.s + (a.t - a.s) * cross(b.t - b.s, b.s - a.s) / cross(b.t - b.s, a.t - a.s);
}
Point intersection(const Segment &a, const Segment &b) {
  assert(has_intersected(a, b));
  if (is_parallel(a, b)) {
    if (sgn(distance(a.s, b.s)) == 0) {
      assert(sgn(dot(a.t - a.s, b.t - a.s)) == -1);
      return a.s;
    } else if (sgn(distance(a.s, b.t)) == 0) {
      assert(sgn(dot(a.t - a.s, b.s - a.s)) == -1);
      return a.s;
    } else if (sgn(distance(a.t, b.s)) == 0) {
      assert(sgn(dot(a.s - a.t, b.t - a.t)) == -1);
      return a.t;
    } else if (sgn(distance(a.t, b.t)) == 0) {
      assert(sgn(dot(a.s - a.t, b.s - a.t)) == -1);
      return a.t;
    } else {
      assert(false);
    }
  } else {
    return intersection(Line(a.s, a.t), Line(b.s, b.t));
  }
}
Point intersection(const Line &a, const Segment &b) {
  assert(has_intersected(a, b));
  return intersection(a, Line(b.s, b.t));
}
std::vector<Point> intersection(const Circle &a, const Line &b) {
  Point pro = projection(b, a.p);
  Real nor = (a.p - pro).norm();
  int sign = sgn(a.r - std::sqrt(nor));
  if (sign == -1) return {};
  if (sign == 0) return {pro};
  Point v = (b.t - b.s).unit_vector() * std::sqrt(a.r * a.r - nor);
  return {pro + v, pro - v};
}
std::vector<Point> intersection(const Circle &a, const Segment &b) {
  if (!has_intersected(a, b)) return {};
  std::vector<Point> res = intersection(a, Line(b.s, b.t));
  if (sgn(distance(a.p, b.s) - a.r) != -1 && sgn(distance(a.p, b.t) - a.r) != -1) return res;
  return {sgn(dot(res[0] - b.s, res[0] - b.t)) == 1 ? res[1] : res[0]};
}
std::vector<Point> intersection(const Circle &a, const Circle &b) {
  int sz = sizeof_common_tangent(a, b);
  if (sz == 0 || sz == 4) return {};
  Real alpha = (b.p - a.p).arg();
  if (sz == 1 || sz == 3) return {Point(a.p.x + a.r * std::cos(alpha), a.p.y + a.r * std::sin(alpha))};
  Real dist = (b.p - a.p).norm(), beta = std::acos((dist + a.r * a.r - b.r * b.r) / (2 * std::sqrt(dist) * a.r));
  return {a.p + Point(a.r * std::cos(alpha + beta), a.r * std::sin(alpha + beta)), a.p + Point(a.r * std::cos(alpha - beta), a.r * std::sin(alpha - beta))};
}

Real distance(const Point &a, const Point &b) { return (b - a).abs(); }
Real distance(const Segment &a, const Point &b) {
  Point foot = projection(a, b);
  return has_intersected(a, foot) ? distance(foot, b) : std::min(distance(a.s, b), distance(a.t, b));
}
Real distance(const Segment &a, const Segment &b) { return has_intersected(a, b) ? 0 : std::min({distance(a, b.s), distance(a, b.t), distance(b, a.s), distance(b, a.t)}); }
Real distance(const Line &a, const Point &b) { return distance(projection(a, b), b); }
Real distance(const Line &a, const Segment &b) { return has_intersected(a, b) ? 0 : std::min(distance(a, b.s), distance(a, b.t)); }
Real distance(const Line &a, const Line &b) { return has_intersected(a, b) ? 0 : distance(a, b.s); }

std::vector<Point> tangency(const Circle &a, const Point &b) {
  Real dist = distance(a.p, b);
  int sign = sgn(dist - a.r);
  if (sign == -1) return {};
  if (sign == 0) return {b};
  Real alpha = (b - a.p).arg(), beta = std::acos(a.r / dist);
  return {a.p + Point(a.r * std::cos(alpha + beta), a.r * std::sin(alpha + beta)), a.p + Point(a.r * std::cos(alpha - beta), a.r * std::sin(alpha - beta))};
}
int sizeof_common_tangent(const Circle &a, const Circle &b) {
  Real dist = distance(a.p, b.p);
  int sign = sgn(a.r + b.r - dist);
  if (sign == -1) return 4;
  if (sign == 0) return 3;
  sign = sgn((sgn(a.r - b.r) == -1 ? b.r - a.r : a.r - b.r) - dist);
  if (sign == -1) return 2;
  if (sign == 0) return 1;
  return 0;
}
std::vector<Line> common_tangent(const Circle &a, const Circle &b) {
  std::vector<Line> tangents;
  Real dist = distance(a.p, b.p), argument = (b.p - a.p).arg();
  int sign = sgn(a.r + b.r - dist);
  if (sign == -1) {
    Real ac = std::acos((a.r + b.r) / dist), alpha = argument + ac, cs = std::cos(alpha), sn = std::sin(alpha);
    tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
    alpha = argument - ac; cs = std::cos(alpha); sn = std::sin(alpha);
    tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
  } else if (sign == 0) {
    Point s = a.p + Point(a.r * std::cos(argument), a.r * std::sin(argument));
    tangents.emplace_back(s, s + (b.p - a.p).normal_unit_vector().first);
  }
  if (sgn(b.r - a.r) == -1) {
    sign = sgn(a.r - b.r - dist);
    if (sign == -1) {
      Real at = std::acos((a.r - b.r) / dist), alpha = argument + at, cs = std::cos(alpha), sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(b.r * cs, b.r * sn));
      alpha = argument - at; cs = std::cos(alpha); sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(b.r * cs, b.r * sn));
    } else if (sign == 0) {
      Point s = a.p + Point(a.r * std::cos(argument), a.r * std::sin(argument));
      tangents.emplace_back(s, s + (b.p - a.p).normal_unit_vector().first);
    }
  } else {
    sign = sgn(b.r - a.r - dist);
    if (sign == -1) {
      Real at = std::acos((b.r - a.r) / dist), alpha = argument - at, cs = std::cos(alpha), sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(-a.r * cs, -a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
      alpha = argument + at; cs = std::cos(alpha); sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(-a.r * cs, -a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
    } else if (sign == 0) {
      Point s = b.p + Point(-b.r * std::cos(argument), -b.r * std::sin(argument));
      tangents.emplace_back(s, s + (a.p - b.p).normal_unit_vector().first);
    }
  }
  return tangents;
}

Real intersection_area(const Circle &a, const Circle &b) {
  Real nor = (b.p - a.p).norm(), dist = std::sqrt(nor);
  if (sgn(a.r + b.r - dist) != 1) return 0;
  if (sgn(std::abs(a.r - b.r) - dist) != -1) return std::min(a.r, b.r) * std::min(a.r, b.r) * PI;
  Real alpha = std::acos((nor + a.r * a.r - b.r * b.r) / (2 * dist * a.r)), beta = std::acos((nor + b.r * b.r - a.r * a.r) / (2 * dist * b.r));
  return (alpha - std::sin(alpha + alpha) * 0.5) * a.r * a.r + (beta - std::sin(beta + beta) * 0.5) * b.r * b.r;
}

using Polygon = std::vector<Point>;

Real area(const Polygon &a) {
  int n = a.size();
  Real res = 0;
  for (int i = 0; i < n; ++i) res += cross(a[i], a[(i + 1) % n]);
  return res * 0.5;
}

Point centroid(const Polygon &a) {
  Point res(0, 0);
  int n = a.size();
  Real den = 0;
  for (int i = 0; i < n; ++i) {
    Real cro = cross(a[i], a[(i + 1) % n]);
    res += (a[i] + a[(i + 1) % n]) / 3 * cro;
    den += cro;
  }
  return res / den;
}

int is_contained(const Polygon &a, const Point &b) {
  int n = a.size();
  bool is_in = false;
  for (int i = 0; i < n; ++i) {
    Point p = a[i] - b, q = a[(i + 1) % n] - b;
    if (sgn(q.y - p.y) == -1) std::swap(p, q);
    int sign = sgn(cross(p, q));
    if (sign == 1 && sgn(p.y) != 1 && sgn(q.y) == 1) is_in = !is_in;
    if (sign == 0 && sgn(dot(p, q)) != 1) return 1;
  }
  return is_in ? 2 : 0;
}

bool is_convex(const Polygon &a) {
  int n = a.size();
  for (int i = 0; i < n; ++i) {
    if (ccw(a[(i - 1 + n) % n], a[i], a[(i + 1) % n]) == -1) return false;
  }
  return true;
}

Polygon monotone_chain(std::vector<Point> ps, bool tight = true) {
  std::sort(ps.begin(), ps.end());
  int n = ps.size(), idx = 0;
  Polygon convex_hull(n << 1);
  for (int i = 0; i < n; convex_hull[idx++] = ps[i++]) {
    while (idx >= 2 && sgn(cross(convex_hull[idx - 1] - convex_hull[idx - 2], ps[i] - convex_hull[idx - 1])) < tight) --idx;
  }
  for (int i = n - 2, border = idx + 1; i >= 0; convex_hull[idx++] = ps[i--]) {
    while (idx >= border && sgn(cross(convex_hull[idx - 1] - convex_hull[idx - 2], ps[i] - convex_hull[idx - 1])) < tight) --idx;
  }
  convex_hull.resize(idx - 1);
  return convex_hull;
}

Polygon cut_convex(const Polygon &a, const Line &b) {
  int n = a.size();
  Polygon res;
  for (int i = 0; i < n; ++i) {
    int c = ccw(b.s, b.t, a[i]);
    if (c != -1) res.emplace_back(a[i]);
    if (c * ccw(b.s, b.t, a[(i + 1) % n]) == -1) res.emplace_back(intersection(Line(a[i], a[(i + 1) % n]), b));
  }
  if (res.size() < 3) res.clear();
  return res;
}

std::pair<Point, Point> rotating_calipers(const Polygon &a) {
  int n = a.size(), high = 0, low = 0;
  if (n <= 2) {
    assert(n == 2);
    return {a[0], a[1]};
  }
  for (int i = 1; i < n; ++i) {
    if (a[i].y > a[high].y) high = i;
    if (a[i].y < a[low].y) low = i;
  }
  Real max_norm = (a[high] - a[low]).norm();
  int i = high, j = low, max_i = i, max_j = j;
  do {
    ((sgn(cross(a[(i + 1) % n] - a[i], a[(j + 1) % n] - a[j])) != -1 ? j : i) += 1) %= n;
    Real tmp = (a[j] - a[i]).norm();
    if (sgn(tmp - max_norm) == 1) {
      max_norm = tmp;
      max_i = i; max_j = j;
    }
  } while (i != high || j != low);
  return {a[max_i], a[max_j]};
}
}  // geometry

int chromatic_number(const std::vector<std::vector<int>> &graph) {
  int n = graph.size();
  std::vector<int> adj(n, 0);
  for (int i = 0; i < n; ++i) for (int e : graph[i]) adj[i] |= 1 << e;
  std::vector<int> indep(1 << n);
  indep[0] = 1;
  for (int i = 1; i < (1 << n); ++i) {
    int ver = __builtin_ctz(i);
    indep[i] = indep[i ^ (1 << ver)] + indep[(i ^ (1 << ver)) & ~adj[ver]];
  }
  int res = n;
  // This implement is too slow.
  // for (int md : vector<int>{1000000007, 1000000011}) {
  //   std::vector<long long> f(1 << n);
  //   for (int i = 0; i < (1 << n); ++i) f[i] = (n - __builtin_popcount(i)) & 1 ? md - 1 : 1;
  //   for (int c = 1; c < res; ++c) {
  //     long long pat = 0;
  //     for (int i = 0; i < (1 << n); ++i) {
  //       (f[i] *= indep[i]) %= md;
  //       pat += f[i];
  //     }
  //     if (pat % md > 0) {
  //       res = c;
  //       break;
  //     }
  //   }
  // }
  constexpr int MOD1 = 1000000007, MOD2 = 1000000011;
  std::vector<long long> f1(1 << n);
  for (int i = 0; i < (1 << n); ++i) f1[i] = (n - __builtin_popcount(i)) & 1 ? MOD1 - 1 : 1;
  for (int c = 1; c < res; ++c) {
    long long pat = 0;
    for (int i = 0; i < (1 << n); ++i) {
      (f1[i] *= indep[i]) %= MOD1;
      pat += f1[i];
    }
    if (pat % MOD1 > 0) {
      res = c;
      break;
    }
  }
  std::vector<long long> f2(1 << n);
  for (int i = 0; i < (1 << n); ++i) f2[i] = (n - __builtin_popcount(i)) & 1 ? MOD2 - 1 : 1;
  for (int c = 1; c < res; ++c) {
    long long pat = 0;
    for (int i = 0; i < (1 << n); ++i) {
      (f2[i] *= indep[i]) %= MOD2;
      pat += f2[i];
    }
    if (pat % MOD2 > 0) {
      res = c;
      break;
    }
  }
  return res;
}

int main() {
  while (true) {
    int n; cin >> n;
    if (n == 0) break;
    vector<vector<geometry::Segment>> line(n);
    REP(i, n) {
      int s; cin >> s;
      vector<int> x(s), y(s); REP(j, s) cin >> x[j] >> y[j];
      FOR(j, 1, s) line[i].emplace_back(geometry::Point(x[j - 1], y[j - 1]), geometry::Point(x[j], y[j]));
    }
    vector<vector<int>> graph(n);
    REP(i, n) FOR(j, i + 1, n) {
      bool is_crossing = false;
      REP(k, line[i].size()) REP(l, line[j].size()) is_crossing |= geometry::has_intersected(line[i][k], line[j][l]);
      if (is_crossing) {
        graph[i].emplace_back(j);
        graph[j].emplace_back(i);
      }
    }
    cout << chromatic_number(graph) << '\n';
  }
  return 0;
}
accuracy: 1
time_ms: 2290
memory_kb: 89176
final_rank: 20

code_snippet: #define _USE_MATH_DEFINES
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
using ll = long long;
constexpr int INF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr double EPS = 1e-8;
constexpr int MOD = 1000000007;
// constexpr int MOD = 998244353;
constexpr int dy[] = {1, 0, -1, 0}, dx[] = {0, -1, 0, 1};
constexpr int dy8[] = {1, 1, 0, -1, -1, -1, 0, 1}, dx8[] = {0, -1, -1, -1, 0, 1, 1, 1};
template <typename T, typename U> inline bool chmax(T &a, U b) { return a < b ? (a = b, true) : false; }
template <typename T, typename U> inline bool chmin(T &a, U b) { return a > b ? (a = b, true) : false; }
struct IOSetup {
  IOSetup() {
    std::cin.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    std::cout << fixed << setprecision(20);
  }
} iosetup;

namespace geometry {
using Real = double;
constexpr long double PI = 3.14159265358979323846;

int sgn(Real x) {
  constexpr Real EPS = 1e-8;
  return x > EPS ? 1 : x < -EPS ? -1 : 0;
}

Real degree_to_radian(Real d) { return d * PI / 180; }
Real radian_to_degree(Real r) { return r * 180 / PI; }

struct Point {
  Real x, y;
  Point(Real x = 0, Real y = 0) : x(x), y(y) {}
  Real abs() const { return std::sqrt(norm()); }
  Real arg() const { Real res = std::atan2(y, x); return res < 0 ? res + PI * 2 : res; }
  Real norm() const { return x * x + y * y; }
  Point rotate(Real angle) const { Real cs = std::cos(angle), sn = std::sin(angle); return Point(x * cs - y * sn, x * sn + y * cs); }
  Point unit_vector() const { Real a = abs(); return Point(x / a, y / a); }
  std::pair<Point, Point> normal_unit_vector() const { Point p = unit_vector(); return {Point(-p.y, p.x), Point(p.y, -p.x)}; }
  Point &operator+=(const Point &p) { x += p.x; y += p.y; return *this; }
  Point &operator-=(const Point &p) { x -= p.x; y -= p.y; return *this; }
  Point &operator*=(Real k) { x *= k; y *= k; return *this; }
  Point &operator/=(Real k) { x /= k; y /= k; return *this; }
  bool operator<(const Point &p) const { int x_sgn = sgn(p.x - x); return x_sgn != 0 ? x_sgn == 1 : sgn(p.y - y) == 1; }
  bool operator<=(const Point &p) const { return !(p < *this); }
  bool operator>(const Point &p) const { return p < *this; }
  bool operator>=(const Point &p) const { return !(*this < p); }
  Point operator+() const { return *this; }
  Point operator-() const { return Point(-x, -y); }
  Point operator+(const Point &p) const { return Point(*this) += p; }
  Point operator-(const Point &p) const { return Point(*this) -= p; }
  Point operator*(Real k) const { return Point(*this) *= k; }
  Point operator/(Real k) const { return Point(*this) /= k; }
  friend std::ostream &operator<<(std::ostream &os, const Point &p) { return os << '(' << p.x << ", " << p.y << ')'; }
  friend std::istream &operator>>(std::istream &is, Point &p) { Real x, y; is >> x >> y; p = Point(x, y); return is; }
};

struct Segment {
  Point s, t;
  Segment(const Point &s = {0, 0}, const Point &t = {0, 0}) : s(s), t(t) {}
};
struct Line : Segment {
  using Segment::Segment;
  Line(Real a, Real b, Real c) {
    if (sgn(a) == 0) {
      s = Point(0, -c / b); t = Point(1, s.y);
    } else if (sgn(b) == 0) {
      s = Point(-c / a, 0); t = Point(s.x, 1);
    } else if (sgn(c) == 0) {
      s = Point(0, 0); t = Point(1, -a / b);
    } else {
      s = Point(0, -c / b); t = Point(-c / a, 0);
    }
  }
};

struct Circle {
  Point p; Real r;
  Circle(const Point &p = {0, 0}, Real r = 0) : p(p), r(r) {}
};

Real cross(const Point &a, const Point &b) { return a.x * b.y - a.y * b.x; }
Real dot(const Point &a, const Point &b) { return a.x * b.x + a.y * b.y; }

int ccw(const Point &a, const Point &b, const Point &c) {
  Point ab = b - a, ac = c - a;
  int sign = sgn(cross(ab, ac));
  if (sign == 0) {
    if (sgn(dot(ab, ac)) == -1) return 2;
    if (sgn(ac.norm() - ab.norm()) == 1) return -2;
  }
  return sign;
}

Real get_angle(const Point &a, const Point &b, const Point &c) {
  Real ba_arg = (a - b).arg(), bc_arg = (c - b).arg();
  if (ba_arg > bc_arg) std::swap(ba_arg, bc_arg);
  return std::min(bc_arg - ba_arg, static_cast<Real>(PI * 2 - (bc_arg - ba_arg)));
}

Real closest_pair(std::vector<Point> ps) {
  int n = ps.size();
  assert(n > 1);
  std::sort(ps.begin(), ps.end());
  std::function<Real(int, int)> rec = [&ps, &rec](int left, int right) -> Real {
    int mid = (left + right) >> 1;
    Real x_mid = ps[mid].x, d = std::numeric_limits<Real>::max();
    if (left + 1 < mid) {
      Real tmp = rec(left, mid);
      if (tmp < d) d = tmp;
    }
    if (mid + 1 < right) {
      Real tmp = rec(mid, right);
      if (tmp < d) d = tmp;
    }
    std::inplace_merge(ps.begin() + left, ps.begin() + mid, ps.begin() + right, [&](const Point &a, const Point &b) -> bool { return sgn(b.y - a.y) == 1; });
    std::vector<Point> tmp;
    for (int i = left; i < right; ++i) {
      if (sgn(std::abs(ps[i].x - x_mid) - d) == 1) continue;
      for (int j = static_cast<int>(tmp.size()) - 1; j >= 0; --j) {
        Point now = ps[i] - tmp[j];
        if (sgn(now.y - d) == 1) break;
        Real tmp = now.abs();
        if (tmp < d) d = tmp;
      }
      tmp.emplace_back(ps[i]);
    }
    return d;
  };
  return rec(0, n);
}

Point projection(const Segment &a, const Point &b) { return a.s + (a.t - a.s) * dot(a.t - a.s, b - a.s) / (a.t - a.s).norm(); }
Point reflection(const Segment &a, const Point &b) { return projection(a, b) * 2 - b; }

bool is_parallel(const Segment &a, const Segment &b) { return sgn(cross(a.t - a.s, b.t - b.s)) == 0; }
bool is_orthogonal(const Segment &a, const Segment &b) { return sgn(dot(a.t - a.s, b.t - b.s)) == 0; }

Real distance(const Point&, const Point&);
Real distance(const Segment&, const Point&);
Real distance(const Line&, const Point&);
int sizeof_common_tangent(const Circle&, const Circle&);
bool has_intersected(const Segment &a, const Point &b) { return ccw(a.s, a.t, b) == 0; }
bool has_intersected(const Segment &a, const Segment &b) { return ccw(a.s, a.t, b.s) * ccw(a.s, a.t, b.t) <= 0 && ccw(b.s, b.t, a.s) * ccw(b.s, b.t, a.t) <= 0; }
bool has_intersected(const Line &a, const Point &b) { int c = ccw(a.s, a.t, b); return c != 1 && c != -1; }
bool has_intersected(const Line &a, const Segment &b) { return ccw(a.s, a.t, b.s) * ccw(a.s, a.t, b.t) != 1; }
bool has_intersected(const Line &a, const Line &b) { return sgn(cross(a.t - a.s, b.t - b.s)) != 0 || sgn(cross(a.t - a.s, b.s - a.s)) == 0; }
bool has_intersected(const Circle &a, const Point &b) { return sgn(distance(a.p, b) - a.r) == 0; }
bool has_intersected(const Circle &a, const Segment &b) { return sgn(a.r - distance(b, a.p)) != -1 && sgn(std::max(distance(a.p, b.s), distance(a.p, b.t)) - a.r) != -1; }
bool has_intersected(const Circle &a, const Line &b) { return sgn(a.r - distance(b, a.p)) != -1; }
bool has_intersected(const Circle &a, const Circle &b) { return sizeof_common_tangent(a, b) > 0; }

Point intersection(const Line &a, const Line &b) {
  assert(has_intersected(a, b) && !is_parallel(a, b));
  return a.s + (a.t - a.s) * cross(b.t - b.s, b.s - a.s) / cross(b.t - b.s, a.t - a.s);
}
Point intersection(const Segment &a, const Segment &b) {
  assert(has_intersected(a, b));
  if (is_parallel(a, b)) {
    if (sgn(distance(a.s, b.s)) == 0) {
      assert(sgn(dot(a.t - a.s, b.t - a.s)) == -1);
      return a.s;
    } else if (sgn(distance(a.s, b.t)) == 0) {
      assert(sgn(dot(a.t - a.s, b.s - a.s)) == -1);
      return a.s;
    } else if (sgn(distance(a.t, b.s)) == 0) {
      assert(sgn(dot(a.s - a.t, b.t - a.t)) == -1);
      return a.t;
    } else if (sgn(distance(a.t, b.t)) == 0) {
      assert(sgn(dot(a.s - a.t, b.s - a.t)) == -1);
      return a.t;
    } else {
      assert(false);
    }
  } else {
    return intersection(Line(a.s, a.t), Line(b.s, b.t));
  }
}
Point intersection(const Line &a, const Segment &b) {
  assert(has_intersected(a, b));
  return intersection(a, Line(b.s, b.t));
}
std::vector<Point> intersection(const Circle &a, const Line &b) {
  Point pro = projection(b, a.p);
  Real nor = (a.p - pro).norm();
  int sign = sgn(a.r - std::sqrt(nor));
  if (sign == -1) return {};
  if (sign == 0) return {pro};
  Point v = (b.t - b.s).unit_vector() * std::sqrt(a.r * a.r - nor);
  return {pro + v, pro - v};
}
std::vector<Point> intersection(const Circle &a, const Segment &b) {
  if (!has_intersected(a, b)) return {};
  std::vector<Point> res = intersection(a, Line(b.s, b.t));
  if (sgn(distance(a.p, b.s) - a.r) != -1 && sgn(distance(a.p, b.t) - a.r) != -1) return res;
  return {sgn(dot(res[0] - b.s, res[0] - b.t)) == 1 ? res[1] : res[0]};
}
std::vector<Point> intersection(const Circle &a, const Circle &b) {
  int sz = sizeof_common_tangent(a, b);
  if (sz == 0 || sz == 4) return {};
  Real alpha = (b.p - a.p).arg();
  if (sz == 1 || sz == 3) return {Point(a.p.x + a.r * std::cos(alpha), a.p.y + a.r * std::sin(alpha))};
  Real dist = (b.p - a.p).norm(), beta = std::acos((dist + a.r * a.r - b.r * b.r) / (2 * std::sqrt(dist) * a.r));
  return {a.p + Point(a.r * std::cos(alpha + beta), a.r * std::sin(alpha + beta)), a.p + Point(a.r * std::cos(alpha - beta), a.r * std::sin(alpha - beta))};
}

Real distance(const Point &a, const Point &b) { return (b - a).abs(); }
Real distance(const Segment &a, const Point &b) {
  Point foot = projection(a, b);
  return has_intersected(a, foot) ? distance(foot, b) : std::min(distance(a.s, b), distance(a.t, b));
}
Real distance(const Segment &a, const Segment &b) { return has_intersected(a, b) ? 0 : std::min({distance(a, b.s), distance(a, b.t), distance(b, a.s), distance(b, a.t)}); }
Real distance(const Line &a, const Point &b) { return distance(projection(a, b), b); }
Real distance(const Line &a, const Segment &b) { return has_intersected(a, b) ? 0 : std::min(distance(a, b.s), distance(a, b.t)); }
Real distance(const Line &a, const Line &b) { return has_intersected(a, b) ? 0 : distance(a, b.s); }

std::vector<Point> tangency(const Circle &a, const Point &b) {
  Real dist = distance(a.p, b);
  int sign = sgn(dist - a.r);
  if (sign == -1) return {};
  if (sign == 0) return {b};
  Real alpha = (b - a.p).arg(), beta = std::acos(a.r / dist);
  return {a.p + Point(a.r * std::cos(alpha + beta), a.r * std::sin(alpha + beta)), a.p + Point(a.r * std::cos(alpha - beta), a.r * std::sin(alpha - beta))};
}
int sizeof_common_tangent(const Circle &a, const Circle &b) {
  Real dist = distance(a.p, b.p);
  int sign = sgn(a.r + b.r - dist);
  if (sign == -1) return 4;
  if (sign == 0) return 3;
  sign = sgn((sgn(a.r - b.r) == -1 ? b.r - a.r : a.r - b.r) - dist);
  if (sign == -1) return 2;
  if (sign == 0) return 1;
  return 0;
}
std::vector<Line> common_tangent(const Circle &a, const Circle &b) {
  std::vector<Line> tangents;
  Real dist = distance(a.p, b.p), argument = (b.p - a.p).arg();
  int sign = sgn(a.r + b.r - dist);
  if (sign == -1) {
    Real ac = std::acos((a.r + b.r) / dist), alpha = argument + ac, cs = std::cos(alpha), sn = std::sin(alpha);
    tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
    alpha = argument - ac; cs = std::cos(alpha); sn = std::sin(alpha);
    tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
  } else if (sign == 0) {
    Point s = a.p + Point(a.r * std::cos(argument), a.r * std::sin(argument));
    tangents.emplace_back(s, s + (b.p - a.p).normal_unit_vector().first);
  }
  if (sgn(b.r - a.r) == -1) {
    sign = sgn(a.r - b.r - dist);
    if (sign == -1) {
      Real at = std::acos((a.r - b.r) / dist), alpha = argument + at, cs = std::cos(alpha), sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(b.r * cs, b.r * sn));
      alpha = argument - at; cs = std::cos(alpha); sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(a.r * cs, a.r * sn), b.p + Point(b.r * cs, b.r * sn));
    } else if (sign == 0) {
      Point s = a.p + Point(a.r * std::cos(argument), a.r * std::sin(argument));
      tangents.emplace_back(s, s + (b.p - a.p).normal_unit_vector().first);
    }
  } else {
    sign = sgn(b.r - a.r - dist);
    if (sign == -1) {
      Real at = std::acos((b.r - a.r) / dist), alpha = argument - at, cs = std::cos(alpha), sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(-a.r * cs, -a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
      alpha = argument + at; cs = std::cos(alpha); sn = std::sin(alpha);
      tangents.emplace_back(a.p + Point(-a.r * cs, -a.r * sn), b.p + Point(-b.r * cs, -b.r * sn));
    } else if (sign == 0) {
      Point s = b.p + Point(-b.r * std::cos(argument), -b.r * std::sin(argument));
      tangents.emplace_back(s, s + (a.p - b.p).normal_unit_vector().first);
    }
  }
  return tangents;
}

Real intersection_area(const Circle &a, const Circle &b) {
  Real nor = (b.p - a.p).norm(), dist = std::sqrt(nor);
  if (sgn(a.r + b.r - dist) != 1) return 0;
  if (sgn(std::abs(a.r - b.r) - dist) != -1) return std::min(a.r, b.r) * std::min(a.r, b.r) * PI;
  Real alpha = std::acos((nor + a.r * a.r - b.r * b.r) / (2 * dist * a.r)), beta = std::acos((nor + b.r * b.r - a.r * a.r) / (2 * dist * b.r));
  return (alpha - std::sin(alpha + alpha) * 0.5) * a.r * a.r + (beta - std::sin(beta + beta) * 0.5) * b.r * b.r;
}

using Polygon = std::vector<Point>;

Real area(const Polygon &a) {
  int n = a.size();
  Real res = 0;
  for (int i = 0; i < n; ++i) res += cross(a[i], a[(i + 1) % n]);
  return res * 0.5;
}

Point centroid(const Polygon &a) {
  Point res(0, 0);
  int n = a.size();
  Real den = 0;
  for (int i = 0; i < n; ++i) {
    Real cro = cross(a[i], a[(i + 1) % n]);
    res += (a[i] + a[(i + 1) % n]) / 3 * cro;
    den += cro;
  }
  return res / den;
}

int is_contained(const Polygon &a, const Point &b) {
  int n = a.size();
  bool is_in = false;
  for (int i = 0; i < n; ++i) {
    Point p = a[i] - b, q = a[(i + 1) % n] - b;
    if (sgn(q.y - p.y) == -1) std::swap(p, q);
    int sign = sgn(cross(p, q));
    if (sign == 1 && sgn(p.y) != 1 && sgn(q.y) == 1) is_in = !is_in;
    if (sign == 0 && sgn(dot(p, q)) != 1) return 1;
  }
  return is_in ? 2 : 0;
}

bool is_convex(const Polygon &a) {
  int n = a.size();
  for (int i = 0; i < n; ++i) {
    if (ccw(a[(i - 1 + n) % n], a[i], a[(i + 1) % n]) == -1) return false;
  }
  return true;
}

Polygon monotone_chain(std::vector<Point> ps, bool tight = true) {
  std::sort(ps.begin(), ps.end());
  int n = ps.size(), idx = 0;
  Polygon convex_hull(n << 1);
  for (int i = 0; i < n; convex_hull[idx++] = ps[i++]) {
    while (idx >= 2 && sgn(cross(convex_hull[idx - 1] - convex_hull[idx - 2], ps[i] - convex_hull[idx - 1])) < tight) --idx;
  }
  for (int i = n - 2, border = idx + 1; i >= 0; convex_hull[idx++] = ps[i--]) {
    while (idx >= border && sgn(cross(convex_hull[idx - 1] - convex_hull[idx - 2], ps[i] - convex_hull[idx - 1])) < tight) --idx;
  }
  convex_hull.resize(idx - 1);
  return convex_hull;
}

Polygon cut_convex(const Polygon &a, const Line &b) {
  int n = a.size();
  Polygon res;
  for (int i = 0; i < n; ++i) {
    int c = ccw(b.s, b.t, a[i]);
    if (c != -1) res.emplace_back(a[i]);
    if (c * ccw(b.s, b.t, a[(i + 1) % n]) == -1) res.emplace_back(intersection(Line(a[i], a[(i + 1) % n]), b));
  }
  if (res.size() < 3) res.clear();
  return res;
}

std::pair<Point, Point> rotating_calipers(const Polygon &a) {
  int n = a.size(), high = 0, low = 0;
  if (n <= 2) {
    assert(n == 2);
    return {a[0], a[1]};
  }
  for (int i = 1; i < n; ++i) {
    if (a[i].y > a[high].y) high = i;
    if (a[i].y < a[low].y) low = i;
  }
  Real max_norm = (a[high] - a[low]).norm();
  int i = high, j = low, max_i = i, max_j = j;
  do {
    ((sgn(cross(a[(i + 1) % n] - a[i], a[(j + 1) % n] - a[j])) != -1 ? j : i) += 1) %= n;
    Real tmp = (a[j] - a[i]).norm();
    if (sgn(tmp - max_norm) == 1) {
      max_norm = tmp;
      max_i = i; max_j = j;
    }
  } while (i != high || j != low);
  return {a[max_i], a[max_j]};
}
}  // geometry

int chromatic_number(const std::vector<std::vector<int>> &graph) {
  int n = graph.size();
  std::vector<int> adj(n, 0);
  for (int i = 0; i < n; ++i) for (int e : graph[i]) adj[i] |= 1 << e;
  std::vector<int> indep(1 << n);
  indep[0] = 1;
  for (int i = 1; i < (1 << n); ++i) {
    int ver = __builtin_ctz(i);
    indep[i] = indep[i ^ (1 << ver)] + indep[(i ^ (1 << ver)) & ~adj[ver]];
  }
  int res = n;
  for (int md : {1000000007}) {
    std::vector<long long> f(1 << n);
    for (int i = 0; i < (1 << n); ++i) f[i] = (n - __builtin_popcount(i)) & 1 ? md - 1 : 1;
    for (int c = 1; c < res; ++c) {
      long long pat = 0;
      for (int i = 0; i < (1 << n); ++i) {
        (f[i] *= indep[i]) %= md;
        pat += f[i];
      }
      if (pat % md > 0) {
        res = c;
        break;
      }
    }
  }
  return res;
}

int main() {
  while (true) {
    int n; cin >> n;
    if (n == 0) break;
    vector<vector<geometry::Segment>> line(n);
    REP(i, n) {
      int s; cin >> s;
      vector<int> x(s), y(s); REP(j, s) cin >> x[j] >> y[j];
      FOR(j, 1, s) line[i].emplace_back(geometry::Point(x[j - 1], y[j - 1]), geometry::Point(x[j], y[j]));
    }
    vector<vector<int>> graph(n);
    REP(i, n) FOR(j, i + 1, n) {
      bool is_crossing = false;
      REP(k, line[i].size()) REP(l, line[j].size()) is_crossing |= geometry::has_intersected(line[i][k], line[j][l]);
      if (is_crossing) {
        graph[i].emplace_back(j);
        graph[j].emplace_back(i);
      }
    }
    cout << chromatic_number(graph) << '\n';
  }
  return 0;
}
accuracy: 1
time_ms: 1200
memory_kb: 60536
final_rank: 11

code_snippet: //
// 彩色数を求める O(N2^N) のアルゴリズム
//
// cf.
//   指数時間アルゴリズム入門
//     
//
// verified
//   AOJ 2136 Webby Subway
//     http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2136
//


#include <iostream>
#include <vector>
#include <cmath>
#include <iomanip>
using namespace std;


// 彩色数
long long modpow(long long a, long long n, long long MOD) {
    long long res = 1;
    while (n > 0) {
        if (n & 1) res = res * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return res;
}
int chromatic_number(const vector<vector<int> > &G) {
    const int MOD = 1000000007;
    int n = (int)G.size();
    vector<int> neighbor(n, 0);
    for (int i = 0; i < n; ++i) {
        int S = (1<<i);
        for (int j = 0; j < n; ++j)
            if (G[i][j])
                S |= (1<<j);
        neighbor[i] = S;
    }
    
    // I[S] := #. of inndepndet subset of S
    vector<int> I(1<<n);
    I[0] = 1;
    for (int S = 1; S < (1<<n); ++S) {
        int v = __builtin_ctz(S);
        I[S] = I[S & ~(1<<v)] + I[S & ~neighbor[v]];
    }
    int low = 0, high = n;
    while (high - low > 1) {
        int mid = (low + high) >> 1;
        
        // g[S] := #. of "k independent sets" which cover S just
        // f[S] := #. of "k independent sets" which consist of subseta of S
        // then
        //   f[S] = sum_{T in S} g(T)
        //   g[S] = sum_{T in S} (-1)^(|S|-|T|)f[T]
        long long g = 0;
        for (int S = 0; S < (1<<n); ++S) {
            if ((n - __builtin_popcount(S)) & 1) g -= modpow(I[S], mid, MOD);
            else g += modpow(I[S], mid, MOD);
            g = (g % MOD + MOD) % MOD;
        }
        if (g != 0) high = mid;
        else low = mid;
    }
    return high;
}



////////////////////////////
// solver
////////////////////////////

using DD = double;
const DD INF = 1LL<<60;      // to be set appropriately
const DD EPS = 1e-10;        // to be set appropriately
const DD PI = acos(-1.0);
DD torad(int deg) {return (DD)(deg) * PI / 180;}
DD todeg(DD ang) {return ang * 180 / PI;}

/* Point */
struct Point {
    DD x, y;
    Point(DD x = 0.0, DD y = 0.0) : x(x), y(y) {}
    friend ostream& operator << (ostream &s, const Point &p) {return s << '(' << p.x << ", " << p.y << ')';}
};
inline Point operator + (const Point &p, const Point &q) {return Point(p.x + q.x, p.y + q.y);}
inline Point operator - (const Point &p, const Point &q) {return Point(p.x - q.x, p.y - q.y);}
inline Point operator * (const Point &p, DD a) {return Point(p.x * a, p.y * a);}
inline Point operator * (DD a, const Point &p) {return Point(a * p.x, a * p.y);}
inline Point operator * (const Point &p, const Point &q) {return Point(p.x * q.x - p.y * q.y, p.x * q.y + p.y * q.x);}
inline Point operator / (const Point &p, DD a) {return Point(p.x / a, p.y / a);}
inline Point conj(const Point &p) {return Point(p.x, -p.y);}
inline Point rot(const Point &p, DD ang) {return Point(cos(ang) * p.x - sin(ang) * p.y, sin(ang) * p.x + cos(ang) * p.y);}
inline Point rot90(const Point &p) {return Point(-p.y, p.x);}
inline DD cross(const Point &p, const Point &q) {return p.x * q.y - p.y * q.x;}
inline DD dot(const Point &p, const Point &q) {return p.x * q.x + p.y * q.y;}
inline DD norm(const Point &p) {return dot(p, p);}
inline DD abs(const Point &p) {return sqrt(dot(p, p));}
inline DD amp(const Point &p) {DD res = atan2(p.y, p.x); if (res < 0) res += PI*2; return res;}
inline bool eq(const Point &p, const Point &q) {return abs(p - q) < EPS;}
inline bool operator < (const Point &p, const Point &q) {return (abs(p.x - q.x) > EPS ? p.x < q.x : p.y < q.y);}
inline bool operator > (const Point &p, const Point &q) {return (abs(p.x - q.x) > EPS ? p.x > q.x : p.y > q.y);}
inline Point operator / (const Point &p, const Point &q) {return p * conj(q) / norm(q);}

/* Line */
struct Line : vector<Point> {
    Line(Point a = Point(0.0, 0.0), Point b = Point(0.0, 0.0)) {
        this->push_back(a);
        this->push_back(b);
    }
    friend ostream& operator << (ostream &s, const Line &l) {return s << '{' << l[0] << ", " << l[1] << '}';}
};

int ccw_for_dis(const Point &a, const Point &b, const Point &c) {
    if (cross(b-a, c-a) > EPS) return 1;
    if (cross(b-a, c-a) < -EPS) return -1;
    if (dot(b-a, c-a) < -EPS) return 2;
    if (norm(b-a) < norm(c-a) - EPS) return -2;
    return 0;
}
bool isinterPS(const Point &p, const Line &s) {
    return (ccw_for_dis(s[0], s[1], p) == 0);
}
bool isinterSS(const Line &s, const Line &t) {
    if (eq(s[0], s[1])) return isinterPS(s[0], t);
    if (eq(t[0], t[1])) return isinterPS(t[0], s);
    return (ccw_for_dis(s[0], s[1], t[0]) * ccw_for_dis(s[0], s[1], t[1]) <= 0 &&
            ccw_for_dis(t[0], t[1], s[0]) * ccw_for_dis(t[0], t[1], s[1]) <= 0);
}

int main() {
    int N;
    while (cin >> N, N) {
        vector<vector<int> > G(N, vector<int>(N, 0));
        vector<vector<Line> > lines(N);
        for (int i = 0; i < N; ++i) {
            int num; cin >> num;
            double x, y; cin >> x >> y;
            for (int j = 1; j < num; ++j) {
                double nx, ny; cin >> nx >> ny;
                Line l(Point(x, y), Point(nx, ny));
                lines[i].push_back(l);
                x = nx, y = ny;
            }
        }
        for (int i = 0; i < N; ++i) {
            for (int j = i + 1; j < N; ++j) {
                bool ok = false;
                for (auto li : lines[i]) {
                    for (auto lj : lines[j]) {
                        if (isinterSS(li, lj)) ok = true;
                    }
                }
                if (ok) G[i][j] = G[j][i] = true;
            }
        }
        cout << chromatic_number(G) << endl;
    }
}
accuracy: 1
time_ms: 2770
memory_kb: 19892
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for(int i = 0; i < n; i++)
#define rep2(i, x, n) for(int i = x; i <= n; i++)
#define rep3(i, x, n) for(int i = x; i >= n; i--)
#define elif else if
#define sp(x) fixed << setprecision(x)
#define pb push_back
#define eb emplace_back
#define all(x) x.begin(), x.end()
#define sz(x) (int)x.size()
using ll = long long;
using pii = pair<int, int>;
using pil = pair<int, ll>;
using pli = pair<ll, int>;
using pll = pair<ll, ll>;
const ll MOD = 1000000007;
//const ll MOD = 998244353;
const int inf = (1<<30)-1;
const ll INF = (1LL<<60)-1;
const double pi = acos(-1.0);
const double EPS = 1e-10;
template<typename T> bool chmax(T &x, const T &y) {return (x < y)? (x = y, true) : false;};
template<typename T> bool chmin(T &x, const T &y) {return (x > y)? (x = y, true) : false;};

using Real = double;
using Point = complex<Real>;

bool eq(Real a, Real b) {return abs(b-a) <= EPS;}

Point operator * (const Point &p, const Real &d){
    return Point(real(p) * d, imag(p) * d);
}

Point operator / (const Point &p, const Real &d){
    return p * (1/d);
}

istream &operator >> (istream &is, Point &p){
    Real a, b;
    is >> a >> b;
    p = Point(a, b);
    return is;
}

ostream &operator << (ostream &os, Point &p){
    return os << sp(10) << real(p) << ' ' << imag(p);
}

Point rotate(const Point &p, const Real &the){
    return p * Point(cos(the), sin(the));
}

struct Line{
    Point a, b;
    Line() = default;
    Line (Point a, Point b) : a(a), b(b) {}
};

struct Segment : Line{
    Segment() = default;
    Segment(Point a, Point b) : Line(a, b) {}
};

struct Circle{
    Point p;
    Real r;
    Circle() = default;
    Circle(Point p, Real r) : p(p), r(r) {}
};

Real dot(const Point &p, const Point &q){
    return real(p) * real(q) + imag(p) * imag(q);
}

Real det(const Point &p, const Point &q){
    return real(p) * imag(q) - imag(p) * real(q);
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_C
int ccw(const Point &a, Point b, Point c){
    b = b - a, c = c - a;
    if(det(b, c) > EPS) return +1; //反時計周り
    if(det(b, c) < -EPS) return -1; //時計回り
    if(dot(b, c) < 0.0) return +2; //逆方向
    if(norm(b) < norm(c)) return -2; //順方向
    return 0; //被り
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_A
bool parallel(const Line &a, const Line &b){
    return eq(det(a.b - a.a, b.b - b.a), 0.0);
}

bool orthogonal(const Line &a, const Line &b) {
    return eq(dot(a.b - a.a, b.b - b.a), 0.0);
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_A
Point projection(const Line &l, const Point &p){
    Real t = dot(p - l.a, l.b - l.a) / norm(l.b - l.a);
    return l.a + (l.b - l.a) * t;
}

Point projection(const Segment &s, const Point &p){
    Real t = dot(p - s.a, s.b - s.a) / norm(s.b - s.a);
    return s.a + (s.b - s.a) * t;
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_1_B
Point reflection(const Line &l, const Point &p){
    return p + (projection(l, p) - p) * 2.0;
}

bool intersect(const Line &l, const Point &p){
    return abs(ccw(l.a, l.b, p)) != 1;
}

bool intersect(const Segment &s, const Point &p){
    return ccw(s.a, s.b, p) == 0;
}

bool intersect(const Line &l, const Line &m){
    if(!eq(det(l.b - l.a, m.b - m.a), 0.0)) return true;
    return eq(det(l.b - l.a, m.b - l.a), 0.0);
}

bool intersect(const Line &l, const Segment &s){
    return det(l.b - l.a, s.a - l.a) * det(l.b - l.a, s.b - l.a) < EPS;
}

Real distance(const Line &l, const Point &p);

bool intersect(const Circle &c, const Line &l){
    return distance(l, c.p) < c.r + EPS;
}

bool intersect(const Circle &c, const Point &p){
    return eq(abs(p - c.p), c.r);
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_B
bool intersect(const Segment &s, const Segment &t){
    if(ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) > 0) return false;
    return ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_A
int intersect(Circle c1, Circle c2){ //共通接線の本数
    if(c1.r < c2.r) swap(c1, c2);
    Real d = abs(c1.p - c2.p);
    if(c1.r + c2.r < d - EPS) return 4;
    if(eq(c1.r + c2.r, d)) return 3;
    if(c1.r - c2.r < d - EPS) return 2;
    if(eq(c1.r - c2.r, d)) return 1;
    return 0; 
}

Real distance(const Point &p, const Point &q){
    return abs(q - p);
}

Real distance(const Line &l, const Point &p){
    return abs(p - Point(projection(l, p)));
}

//https://atcoder.jp/contests/arc042/tasks/arc042_b
Real distance(const Segment &s, const Point &p){
    Point h = projection(s, p);
    if(intersect(s, h)) return abs(h - p);
    return min(abs(s.a - p), abs(s.b - p));
}

Real distance(const Line &l, const Line &m){
    return intersect(l, m)? 0.0 : distance(l, m.a); 
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_D
Real distance(const Segment &s, const Segment &t){
    if(intersect(s, t)) return 0.0;
    return min({distance(s, t.a), distance(s, t.b), distance(t, s.a), distance(t, s.b)});
}

Real distance(const Line &l, const Segment &s){
    if(intersect(l, s)) return 0.0;
    return min(distance(l, s.a), distance(l, s.b));
}

vector<Point> crosspoint(const Line &l, const Line &m){
    vector<Point> ret;
    if(!intersect(l, m)) return ret;
    Real A = det(l.b - l.a, m.b - m.a);
    Real B = det(l.b - l.a, l.b - m.a);
    if(eq(A, 0.0) && eq(B, 0.0)) ret .pb(m.a);
    else ret.pb(m.a + (m.b - m.a) * B / A);
    return ret;
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_2_C
vector<Point> crosspoint(const Segment &s, const Segment &t){
    return crosspoint(Line(s), Line(t));
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_D
vector<Point> crosspoint(const Circle &c, const Line &l){
    Point h = projection(l, c.p);
    Point e = (l.b - l.a) / abs(l.b - l.a);
    vector<Point> ret;
    if(!intersect(c, l)) return ret;
    if(eq(distance(l, c.p), c.r)) ret.pb(h);
    else{
        Real base = sqrt(c.r * c.r - norm(h - c.p));
        ret.pb(h + e * base), ret.pb(h - e * base);
    }
    return ret;
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_E
vector<Point> crosspoint(const Circle &c1, const Circle &c2){
    Real d = abs(c1.p - c2.p);
    Real a = acos((c1.r * c1.r + d * d - c2.r * c2.r) / (2 * c1.r * d));
    Real t = atan2(c2.p.imag() - c1.p.imag(), c2.p.real() - c1.p.real());
    vector<Point> ret;
    if(!intersect(c1, c2)) return ret;
    if(eq(a, 0.0)) ret.pb(Point(c1.p + rotate(Point(c1.r, 0.0), t)));
    else{
        Point p1 = c1.p + rotate(Point(c1.r, 0.0), t + a);
        Point p2 = c1.p + rotate(Point(c1.r, 0.0), t - a);
        ret.pb(p1), ret.pb(p2);
    }
    return ret;
}

Line vertical_bisector(const Point &p, const Point &q){
    Line l;
    l.a = (p + q) / 2.0;
    l.b = l.a + rotate(q - p, pi / 2.0);
    return l;
}

Circle Apollonius(const Point &p, const Point &q, const Real &a, const Real &b){
    Point p1 = (p * b + q * a) / (a + b), p2 = (-p * b + q * a) / (a - b);
    Circle c;
    c.p = (p1 + p2) / 2.0;
    c.r = abs(p1 - p2) / 2.0;
    return c;
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_3_A
Real area(const vector<Point> &p){
    Real ret = 0.0;
    int n = sz(p);
    rep(i, n) ret += det(p[i], p[(i+1)%n]);
    return abs(ret / 2.0);
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_F
vector<Point> tangent(const Circle &c, const Point &p){
    return crosspoint(c, Circle(p, sqrt(norm(p - c.p) - c.r * c.r)));
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_7_G
vector<Line> tangent(Circle c1, Circle c2){
    vector<Line> ret;
    if(c1.r < c2.r) swap(c1, c2);
    Real r = abs(c2.p - c1.p);
    if(eq(r, 0.0)) return ret;
    Point u = (c2.p - c1.p) / r;
    Point v = rotate(u, pi / 2.0);
    for(double s : {1.0, -1.0}){
        Real h = (c1.r + c2.r * s) / r;
        if(eq(abs(h), 1.0)){
            ret.eb(c1.p + u * c1.r, c1.p + (u + v) * c1.r);
        }
        elif(abs(h) < 1.0){
            Point uu = u * h, vv = v * sqrt(1.0 - h*h);
            ret.eb(c1.p + (uu + vv) * c1.r, c2.p - (uu + vv) * c2.r * s);
            ret.eb(c1.p + (uu - vv) * c1.r, c2.p - (uu - vv) * c2.r * s);
        }
    }
    return ret;
}

//http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=CGL_4_A
vector<Point> convex_hull(vector<Point> p){
    int n = sz(p), k = 0;
    sort(all(p), [](const Point &p, const Point &q){
        if(!eq(real(p), real(q))) return real(p) < real(q);
        return imag(p) < imag(q);
    });
    vector<Point> ch(2*n);
    for(int i = 0; i < n; ch[k++] = p[i++]){
        while(k >= 2 && det(ch[k-1] - ch[k-2], p[i] - ch[k-1]) < EPS) k--;
    }
    for(int i = n-2, t = k+1; i >= 0; ch[k++] = p[i--]){
        while(k >= t && det(ch[k-1] - ch[k-2], p[i] - ch[k-1]) < EPS) k--; 
    }
    ch.resize(k-1);
    return ch;
}

template<ll mod>
struct Mod_Int{
    ll x;
    Mod_Int() {}
    Mod_Int(ll y) : x (y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}

    Mod_Int &operator += (const Mod_Int &p){
        x = (x + p.x) % mod;
        return *this;
    }

    Mod_Int &operator -= (const Mod_Int &p){
        x = (x + mod - p.x) % mod;
        return *this;
    }

    Mod_Int &operator *= (const Mod_Int &p){
        x = (x * p.x) % mod;
        return *this;
    }

    Mod_Int &operator /= (const Mod_Int &p){
        *this *= p.inverse();
        return *this;
    }

    Mod_Int &operator ++ () {return *this += Mod_Int(1);}

    Mod_Int operator ++ (int){
        Mod_Int tmp = *this;
        ++*this;
        return tmp;
    }

    Mod_Int &operator -- () {return *this -= Mod_Int(1);}

    Mod_Int operator -- (int){
        Mod_Int tmp = *this;
        --*this;
        return tmp;
    }

    Mod_Int operator - () const {return Mod_Int(-x);}

    Mod_Int operator + (const Mod_Int &p) const {return Mod_Int(*this) += p;}

    Mod_Int operator - (const Mod_Int &p) const {return Mod_Int(*this) -= p;}

    Mod_Int operator * (const Mod_Int &p) const {return Mod_Int(*this) *= p;}

    Mod_Int operator / (const Mod_Int &p) const {return Mod_Int(*this) /= p;}

    bool operator == (const Mod_Int &p) const {return x == p.x;}

    bool operator != (const Mod_Int &p) const {return x != p.x;}

    Mod_Int inverse() const {
        assert(*this != Mod_Int(0));
        return pow(mod-2);
    }

    Mod_Int pow(ll k) const{
        Mod_Int now = *this, ret = 1;
        while(k){
            if(k&1) ret *= now;
            now *= now, k >>= 1;
        }
        return ret;
    }

    friend ostream &operator << (ostream &os, const Mod_Int &p){
        return os << p.x;
    }

    friend istream &operator >> (istream &is, Mod_Int &p){
        ll a;
        is >> a;
        p = Mod_Int<mod>(a);
        return is;
    }
};

using mint = Mod_Int<MOD>;

int main(){
    while(true){
        int N;
        cin >> N;
        if(N == 0) break;
        vector<Segment> s[N];
        rep(i, N){
            int K;
            cin >> K;
            Point p[K];
            rep(j, K) cin >> p[j];
            rep(j, K-1) s[i].pb(Segment(p[j], p[j+1]));
        }
        int list[N];
        fill(list, list+N, 0);
        rep(i, N){
            rep(j, N){
                for(auto &e: s[i]){
                    for(auto &u: s[j]){
                        if(intersect(e, u)) list[i] |= (1<<j);
                    }
                }
            }
        }
        mint dp[1<<N];
        dp[0] = 1;
        rep2(i, 1, (1<<N)-1){
            int j = __builtin_ctz(i);
            dp[i] = dp[i&~(1<<j)]+dp[i&~list[j]];
        }
        int l = 0, r = N; //(l, r]
        while(r-l > 1){
            int m = (l+r)/2;
            mint ans = 0;
            rep(i, 1<<N){
                mint tmp = dp[i].pow(m);
                ans += (__builtin_parity(i)^(N&1)? -tmp : tmp);
            }
            (ans == 0? l : r) = m;
        }
        cout << r << endl;
    }
}
accuracy: 1
time_ms: 3230
memory_kb: 35964
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define ALL(x) x.begin(),x.end()
#define rep(i,n) for(int i=0;i<(n);i++)
#define debug(v) cout<<#v<<":";for(auto x:v){cout<<x<<' ';}cout<<endl;
#define INF 1000000000
#define mod 1000000007
using ll=long long;
const ll LINF=1001002003004005006ll;
int dx[]={1,0,-1,0};
int dy[]={0,1,0,-1};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
template<class T>bool chmax(T &a,const T &b){if(a<b){a=b;return true;}return false;}
template<class T>bool chmin(T &a,const T &b){if(b<a){a=b;return true;}return false;}

//////////////////////////////////////////////////////
using Real=double;
using Point=complex<Real>;
const Real EPS=1e-10;
const Real pi=acosl(-1);
//入出力補助
istream &operator>>(istream &is,Point &p){
    Real a,b;
    is>>a>>b;
    p=Point(a,b);
    return is;
}
ostream &operator<<(ostream &os,Point &p){
    return os<<fixed<<setprecision(12)<<p.real()<<' '<<p.imag();
}
 
inline bool eq(Real a,Real b){
    return fabs(a-b)<EPS;
}
Point operator*(const Point &p,const Real &d){
    return Point(real(p)*d,imag(p)*d);
}
struct Line{
    Point p1,p2;
    Line()=default;
    Line(Point p1,Point p2):p1(p1),p2(p2){}
 
    //Ax + By = C
    Line(Real A,Real B,Real C){
       if(eq(A,0))     p1=Point(0,C/B),p2=Point(1,C/B);
       else if(eq(B,0))p1=Point(C/A,0),p2=Point(C/A,1);
       else            p1=Point(0,C/B),p2=Point(C/A,0);
    }
};
struct Segment:Line{
   Segment()=default;
   Segment(Point p1,Point p2):Line(p1,p2){}
};
struct Circle{
    Point center;
    Real r;
    Circle()=default;
    Circle(Point center,Real r):center(center),r(r){}
};
/////////////////////////////////////////////////////////
 
 
// 点 p を反時計回りに theta 回転
Point rotate(Real theta,const Point &p) {
    return Point(cos(theta)*p.real()-sin(theta)*p.imag(),sin(theta)*p.real()+cos(theta)*p.imag());
}
 
Real radian_to_degree(Real r){
    return r*180.0/pi;
}
 
Real degree_to_radian(Real d){
    return d*pi/180.0;
}
 
//三角形の面積，サラスの公式
Real area_triangle(Point a,Point b,Point c){
    Point x=b-a,y=c-a;
    return fabs(x.real()*y.imag()-x.imag()*y.real())/2;
}
 
//v
//外積
Real cross(Point a,Point b){
    return real(a)*imag(b)-imag(a)*real(b);
}
//v
//内積
Real dot(Point a,Point b) {
    return real(a)*real(b)+imag(a)*imag(b);
}
 
//v
//平行判定，外積0かをみる
bool parallel(Line a,Line b){
    return eq(cross(a.p1-a.p2,b.p1-b.p2),0.0);
}
//v
//垂直判定，内積0かをみる
bool orthogonal(Line a,Line b){
    return eq(dot(a.p1-a.p2,b.p1-b.p2),0.0);
}
 
//v
//正射影，pからlに下した垂線の足を求める
Point projection(Line l,Point p){
    //ベクトルl上のどの位置に垂線の足が来るか求める
    Real k=dot(l.p1-l.p2,p-l.p1)/norm(l.p1-l.p2);
    return l.p1+(l.p1-l.p2)*k;
}
Point projection(Segment l,Point p){
    Real k=dot(l.p1-l.p2,p-l.p1)/norm(l.p1-l.p2);
    return l.p1+(l.p1-l.p2)*k;
}
 
//v
//反射，直線lに関し点pと線対称な点を返す
Point reflection(Line l,Point p){
    Point h=projection(l,p);
    return (p+(h-p)+(h-p));
}
Point reflection(Segment l,Point p){
    Point h=projection(l,p);
    return (p+(h-p)+(h-p));
}
 
//二点間の距離
Real dis(Point a,Point b){
    return abs(a-b);
} 
//点と直線の距離
Real dis(Line l,Point p){
    return abs(p-projection(l,p));
}
 
//v
//COUNTER CLOCKWISE，返す値は↓を参照
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/all/CGL_1_C
int ccw(Point a,Point b,Point c){
    b-=a;c-=a;
    if(cross(b,c)>EPS)       return  1;//COUNTER CLOCKWISE
    else if(cross(b,c)<-EPS) return -1;//CLOCKWISE
    else if(dot(b,c)<0)      return  2;//c--a--b ONLINE BACK
    else if(norm(b)<norm(c)) return -2;//a--b--c ONLINE FRONT
    else                     return  0;//a--c--b ON SEGMENT
}
 
//v
//3点が作る三角形の外心
//面積0の三角形を渡すと分母に面積があるので壊れるかも
Point circumcenter(Point A,Point B,Point C){
    Real S=area_triangle(A,B,C);
    Real a=dis(B,C),b=dis(A,C),c=dis(A,B);
    return A*(a*a*(b*b+c*c-a*a)/(16*S*S))+B*(b*b*(c*c+a*a-b*b)/(16*S*S))+C*(c*c*(a*a+b*b-c*c)/(16*S*S));
}
 
//交差判定
//直線状に乗るか
bool intersect(Line l,Point p){
    return abs(ccw(l.p1,l.p2,p))!=1;
}
//直線の交差判定，外積
bool intersect(Line l1,Line l2){
    return abs(cross(l1.p2-l1.p1,l2.p2-l2.p1))>EPS or
        abs(cross(l1.p2-l1.p1,l2.p2-l1.p1))<EPS;
}
//線分に点が乗るかの判定，ccw
bool intersect(Segment s,Point p){
    return ccw(s.p1,s.p2,p)==0;
}
//直線と線分の交差判定
bool intersect(Line l,Segment s){
    return cross(l.p2-l.p1,s.p1-l.p1)*cross(l.p2-l.p1,s.p2-l.p1)<EPS;
}
//円と直線の交差判定
bool intersect(Circle c,Line l){
    return dis(l,c.center)<=c.r+EPS;
}
//円上かどうか，内部かどうかではない
bool intersect(Circle c,Point p){
    return abs(abs(p-c.center)-c.r)<EPS;
}
//v
//線分と線分の交差判定
bool intersect(Segment s,Segment t){
    return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <=0 and
        ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2)<=0;
}
//線分と円の交差判定，交点の個数を返す
int intersect(Circle c,Segment l){
    Point h=projection(l,c.center);
    //直線まるっと円の外側
    if(norm(h-c.center)-c.r*c.r>EPS)    return 0;
    Real d1=abs(c.center-l.p1),d2=abs(c.center-l.p2);
    //線分が円内
    if(d1<c.r+EPS and d2<c.r+EPS) return 0;
    if((d1<c.r-EPS and d2>c.r+EPS) or (d2<c.r-EPS and d1>c.r+EPS)) return 1;
    //円の外部にまるまるはみ出ていないか
    if(dot(l.p1-h,l.p2-h)<0) return 2;
    return 0;
}
//円と円の位置関係，共通接線の個数を返す
int intersect(Circle c1,Circle c2){
    if(c1.r<c2.r) swap(c1,c2);
    Real d=abs(c1.center-c2.center);
    //2円が離れている
    if(c1.r+c2.r<d)     return 4;
    //2円が外接する
    if(eq(c1.r+c2.r,d)) return 3;
    //2円が交わる
    if(c1.r-c2.r<d)     return 2;
    //円が内接する
    if(eq(c1.r-c2.r,d)) return 1;
    //内包
                        return 0;
}
 
//交点
//線分の交点はintersectをチェックしてokなら直線の交点をやる
//intersectをチェックすること
//v
Point crosspoint(Line l,Line m){
    Real A=cross(m.p2-m.p1,m.p1-l.p1);
    Real B=cross(m.p2-m.p1,l.p2-l.p1);
    if(eq(A,0) and eq(B,0)) return l.p1;
    if(eq(B,0))             throw "NAI";
    return l.p1+A/B*(l.p2-l.p1);    
}
Point crosspoint(Segment l,Segment m){
    return crosspoint(Line(l),Line(m));
}
vector<Point> crosspoint(Circle c,Line l){
    vector<Point> ret;
    Point h=projection(l,c.center);
    Real d=sqrt(c.r*c.r-norm(h-c.center));
    Point e=(l.p2-l.p1)*(1/abs(l.p2-l.p1));
    if(c.r*c.r+EPS<norm(h-c.center)) return ret;
    if(eq(dis(l,c.center),c.r)){
        ret.push_back(h);
        return ret;
    }
    ret.push_back(h+e*d);ret.push_back(h-e*d);
    return ret;
}
//要verify，
vector<Point> crosspoint(Circle c,Segment s){
    Line l=Line(s.p1,s.p2);
    int ko=intersect(c,s);
    if(ko==2) return crosspoint(c,l);
    vector<Point> ret;
    if(ko==0) return ret;
    ret=crosspoint(c,l);
    if(ret.size()==1) return ret;
    vector<Point> rret;
    //交点で挟める方を返す
    if(dot(s.p1-ret[0],s.p2-ret[0])<0)  rret.push_back(ret[0]);
    else                                rret.push_back(ret[1]);
    return rret;
}
//v
vector<Point> crosspoint(Circle c1,Circle c2){
    vector<Point> ret;
    int isec=intersect(c1,c2);
    if(isec==0 or isec==4) return ret;
    Real d=abs(c1.center-c2.center);
    Real a=acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d));
    Real t=atan2(c2.center.imag()-c1.center.imag(),c2.center.real()-c1.center.real());
    ret.push_back(c1.center+Point(cos(t+a)*c1.r,sin(t+a)*c1.r));
    ret.push_back(c1.center+Point(cos(t-a)*c1.r,sin(t-a)*c1.r));
    return ret;
}
 
//v
//点pから引いた円cの接線の接点を返す
vector<Point> tangent(Circle c,Point p){
    return crosspoint(c,Circle(p,sqrt(norm(c.center-p)-c.r*c.r)));
}
//v
//二円の共通接線，Lineの2点は接点を表す
vector<Line> tangent(Circle c1,Circle c2){
    vector<Line> ret;
    if(c1.r<c2.r) swap(c1,c2);
    Real g=norm(c1.center-c2.center);
    //中心が一致するならない
    if(eq(g,0)) return ret;
    Point u=(c2.center-c1.center)/sqrt(g);
    Point v=rotate(pi*0.5,u);
    for(int s:{-1,1}){
        Real h=(c1.r+s*c2.r)/sqrt(g);
        if(eq(1-h*h,0)){
            ret.push_back(Line(c1.center+u*c1.r,c1.center+(u+v)*c1.r));
        }
        else if(1-h*h>0){
            Point uu=u*h,vv=v*sqrt(1-h*h);
            ret.push_back(Line(c1.center+(uu+vv)*c1.r,c2.center-(uu+vv)*c2.r*s));
            ret.push_back(Line(c1.center+(uu-vv)*c1.r,c2.center-(uu-vv)*c2.r*s));
        }
    }
    return ret;
}
 
//v
//最小包含円を返す　計算量は期待値O(n)
Circle MinimumBoundingCircle(vector<Point> v){
    int n=v.size();
  
    //ランダムシャッフル．いぢわるされたくないもんだ
    mt19937 mt(time(0));
    shuffle(v.begin(),v.end(),mt);
    Circle ret(0,0);
    //2点で円を作る
    auto make_circle2=[&](Point a,Point b){
        return Circle((a+b)*0.5,dis(a,b)/2);
    };
    //3点で円を作る
    auto make_circle3=[&](Point A,Point B,Point C){
        Point cent=circumcenter(A,B,C);
        return Circle(cent,dis(cent,A));
    };
    auto isIn=[&](Point a){
        return dis(ret.center,a)<ret.r+EPS;
    };
 
    ret=make_circle2(v[0],v[1]);
    for(int i=2;i<n;i++){
        //v[i]が円に入っていないなら
        if(!isIn(v[i])){
            //円内にないなら点v[i]は必ず円周上に来る
            ret=make_circle2(v[0],v[i]);
            for(int j=1;j<i;j++){
                if(!isIn(v[j])){
                    //この時iとjが円周上を考える
                    ret=make_circle2(v[i],v[j]);
                    //最後の1点の決定
                    for(int k=0;k<j;k++){
                        if(!isIn(v[k])){
                            ret=make_circle3(v[i],v[j],v[k]);
                        }
                    }
                }
            }
        }
    }
    return ret;
}

// 繰り返し二乗法 
ll pow_mod(ll x, ll n){
    if(n==0) return 1;
    ll ret=pow_mod((x*x) % mod, n/2);
    if(n&1) ret=(ret*x)%mod;
    return ret;
}
//mod変えて何回かやったminをとるべきかもしれない
int chromatic_number(const vector<vector<bool>> &g){
    int n=(int)g.size();
    vector<int> es(n,0);
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)es[i]|=(g[i][j]<<j);
    vector<int> I(1<<n);I[0]=1;
    for(int S=1;S<(1<<n);S++){
        int v=__builtin_ctz(S);
        //S/{v}にvを別色で塗る+vをSの関与しない部分集合のどこかに属させる
        I[S]=I[S^(1<<v)]+I[(S^(1<<v))&(~es[v])];
    }
    int lw=0,hi=n;
    while(hi-lw>1){
        int mid=(lw+hi)/2;
        //f[S]:Sの部分集合のうち独立なものk個の組の個数->f[S]=I[S]^k
        //g[S]:k個の独立集合でSを被覆する場合の数，Sの部分集合のfの包除で求まる
        ll g=0;//g[V]について包除
        for(int S=0;S<(1<<n);S++){
            if((n-__builtin_popcount(S))&1) g-=pow_mod(I[S],mid);
            else                            g+=pow_mod(I[S],mid);
            g=(g%mod+mod)%mod;
        }
        if(g!=0)    hi=mid;
        else        lw=mid;
    }
    return hi;
}

int n;
void solve(){
    vector<vector<Segment>> ls(n);
    rep(i,n){
        int m;cin>>m;
        Point pre;
        cin>>pre;
        rep(j,m-1){
            Point p;cin>>p;
            ls[i].push_back(Segment(p,pre));
            pre=p;
        }
    }
    vector<vector<bool>> g(n,vector<bool>(n));
    rep(i,n){
        for(int j=i+1;j<n;j++){
            bool f=false;
            for(auto a:ls[i]){
                for(auto b:ls[j]){
                    if(intersect(a,b)) f=true;
                }
            }
            g[i][j]=f;
            g[j][i]=f;
        }
    }
    cout<<chromatic_number(g)<<endl;
}

signed main(){
    cin.tie(0);
    ios::sync_with_stdio(0);

    while(cin>>n,n){
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 4970
memory_kb: 19644
final_rank: 15

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define ALL(x) x.begin(),x.end()
#define rep(i,n) for(int i=0;i<(n);i++)
#define debug(v) cout<<#v<<":";for(auto x:v){cout<<x<<' ';}cout<<endl;
#define INF 1000000000
#define mod 1000000007
using ll=long long;
const ll LINF=1001002003004005006ll;
int dx[]={1,0,-1,0};
int dy[]={0,1,0,-1};
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
template<class T>bool chmax(T &a,const T &b){if(a<b){a=b;return true;}return false;}
template<class T>bool chmin(T &a,const T &b){if(b<a){a=b;return true;}return false;}

//////////////////////////////////////////////////////
using Real=double;
using Point=complex<Real>;
const Real EPS=1e-10;
const Real pi=acosl(-1);
//入出力補助
istream &operator>>(istream &is,Point &p){
    Real a,b;
    is>>a>>b;
    p=Point(a,b);
    return is;
}
ostream &operator<<(ostream &os,Point &p){
    return os<<fixed<<setprecision(12)<<p.real()<<' '<<p.imag();
}
 
inline bool eq(Real a,Real b){
    return fabs(a-b)<EPS;
}
Point operator*(const Point &p,const Real &d){
    return Point(real(p)*d,imag(p)*d);
}
struct Line{
    Point p1,p2;
    Line()=default;
    Line(Point p1,Point p2):p1(p1),p2(p2){}
 
    //Ax + By = C
    Line(Real A,Real B,Real C){
       if(eq(A,0))     p1=Point(0,C/B),p2=Point(1,C/B);
       else if(eq(B,0))p1=Point(C/A,0),p2=Point(C/A,1);
       else            p1=Point(0,C/B),p2=Point(C/A,0);
    }
};
struct Segment:Line{
   Segment()=default;
   Segment(Point p1,Point p2):Line(p1,p2){}
};
struct Circle{
    Point center;
    Real r;
    Circle()=default;
    Circle(Point center,Real r):center(center),r(r){}
};
/////////////////////////////////////////////////////////
 
 
// 点 p を反時計回りに theta 回転
Point rotate(Real theta,const Point &p) {
    return Point(cos(theta)*p.real()-sin(theta)*p.imag(),sin(theta)*p.real()+cos(theta)*p.imag());
}
 
Real radian_to_degree(Real r){
    return r*180.0/pi;
}
 
Real degree_to_radian(Real d){
    return d*pi/180.0;
}
 
//三角形の面積，サラスの公式
Real area_triangle(Point a,Point b,Point c){
    Point x=b-a,y=c-a;
    return fabs(x.real()*y.imag()-x.imag()*y.real())/2;
}
 
//v
//外積
Real cross(Point a,Point b){
    return real(a)*imag(b)-imag(a)*real(b);
}
//v
//内積
Real dot(Point a,Point b) {
    return real(a)*real(b)+imag(a)*imag(b);
}
 
//v
//平行判定，外積0かをみる
bool parallel(Line a,Line b){
    return eq(cross(a.p1-a.p2,b.p1-b.p2),0.0);
}
//v
//垂直判定，内積0かをみる
bool orthogonal(Line a,Line b){
    return eq(dot(a.p1-a.p2,b.p1-b.p2),0.0);
}
 
//v
//正射影，pからlに下した垂線の足を求める
Point projection(Line l,Point p){
    //ベクトルl上のどの位置に垂線の足が来るか求める
    Real k=dot(l.p1-l.p2,p-l.p1)/norm(l.p1-l.p2);
    return l.p1+(l.p1-l.p2)*k;
}
Point projection(Segment l,Point p){
    Real k=dot(l.p1-l.p2,p-l.p1)/norm(l.p1-l.p2);
    return l.p1+(l.p1-l.p2)*k;
}
 
//v
//反射，直線lに関し点pと線対称な点を返す
Point reflection(Line l,Point p){
    Point h=projection(l,p);
    return (p+(h-p)+(h-p));
}
Point reflection(Segment l,Point p){
    Point h=projection(l,p);
    return (p+(h-p)+(h-p));
}
 
//二点間の距離
Real dis(Point a,Point b){
    return abs(a-b);
} 
//点と直線の距離
Real dis(Line l,Point p){
    return abs(p-projection(l,p));
}
 
//v
//COUNTER CLOCKWISE，返す値は↓を参照
//https://onlinejudge.u-aizu.ac.jp/courses/library/4/CGL/all/CGL_1_C
int ccw(Point a,Point b,Point c){
    b-=a;c-=a;
    if(cross(b,c)>EPS)       return  1;//COUNTER CLOCKWISE
    else if(cross(b,c)<-EPS) return -1;//CLOCKWISE
    else if(dot(b,c)<0)      return  2;//c--a--b ONLINE BACK
    else if(norm(b)<norm(c)) return -2;//a--b--c ONLINE FRONT
    else                     return  0;//a--c--b ON SEGMENT
}
 
//v
//3点が作る三角形の外心
//面積0の三角形を渡すと分母に面積があるので壊れるかも
Point circumcenter(Point A,Point B,Point C){
    Real S=area_triangle(A,B,C);
    Real a=dis(B,C),b=dis(A,C),c=dis(A,B);
    return A*(a*a*(b*b+c*c-a*a)/(16*S*S))+B*(b*b*(c*c+a*a-b*b)/(16*S*S))+C*(c*c*(a*a+b*b-c*c)/(16*S*S));
}
 
//交差判定
//直線状に乗るか
bool intersect(Line l,Point p){
    return abs(ccw(l.p1,l.p2,p))!=1;
}
//直線の交差判定，外積
bool intersect(Line l1,Line l2){
    return abs(cross(l1.p2-l1.p1,l2.p2-l2.p1))>EPS or
        abs(cross(l1.p2-l1.p1,l2.p2-l1.p1))<EPS;
}
//線分に点が乗るかの判定，ccw
bool intersect(Segment s,Point p){
    return ccw(s.p1,s.p2,p)==0;
}
//直線と線分の交差判定
bool intersect(Line l,Segment s){
    return cross(l.p2-l.p1,s.p1-l.p1)*cross(l.p2-l.p1,s.p2-l.p1)<EPS;
}
//円と直線の交差判定
bool intersect(Circle c,Line l){
    return dis(l,c.center)<=c.r+EPS;
}
//円上かどうか，内部かどうかではない
bool intersect(Circle c,Point p){
    return abs(abs(p-c.center)-c.r)<EPS;
}
//v
//線分と線分の交差判定
bool intersect(Segment s,Segment t){
    return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <=0 and
        ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2)<=0;
}
//線分と円の交差判定，交点の個数を返す
int intersect(Circle c,Segment l){
    Point h=projection(l,c.center);
    //直線まるっと円の外側
    if(norm(h-c.center)-c.r*c.r>EPS)    return 0;
    Real d1=abs(c.center-l.p1),d2=abs(c.center-l.p2);
    //線分が円内
    if(d1<c.r+EPS and d2<c.r+EPS) return 0;
    if((d1<c.r-EPS and d2>c.r+EPS) or (d2<c.r-EPS and d1>c.r+EPS)) return 1;
    //円の外部にまるまるはみ出ていないか
    if(dot(l.p1-h,l.p2-h)<0) return 2;
    return 0;
}
//円と円の位置関係，共通接線の個数を返す
int intersect(Circle c1,Circle c2){
    if(c1.r<c2.r) swap(c1,c2);
    Real d=abs(c1.center-c2.center);
    //2円が離れている
    if(c1.r+c2.r<d)     return 4;
    //2円が外接する
    if(eq(c1.r+c2.r,d)) return 3;
    //2円が交わる
    if(c1.r-c2.r<d)     return 2;
    //円が内接する
    if(eq(c1.r-c2.r,d)) return 1;
    //内包
                        return 0;
}
 
//交点
//線分の交点はintersectをチェックしてokなら直線の交点をやる
//intersectをチェックすること
//v
Point crosspoint(Line l,Line m){
    Real A=cross(m.p2-m.p1,m.p1-l.p1);
    Real B=cross(m.p2-m.p1,l.p2-l.p1);
    if(eq(A,0) and eq(B,0)) return l.p1;
    if(eq(B,0))             throw "NAI";
    return l.p1+A/B*(l.p2-l.p1);    
}
Point crosspoint(Segment l,Segment m){
    return crosspoint(Line(l),Line(m));
}
vector<Point> crosspoint(Circle c,Line l){
    vector<Point> ret;
    Point h=projection(l,c.center);
    Real d=sqrt(c.r*c.r-norm(h-c.center));
    Point e=(l.p2-l.p1)*(1/abs(l.p2-l.p1));
    if(c.r*c.r+EPS<norm(h-c.center)) return ret;
    if(eq(dis(l,c.center),c.r)){
        ret.push_back(h);
        return ret;
    }
    ret.push_back(h+e*d);ret.push_back(h-e*d);
    return ret;
}
//要verify，
vector<Point> crosspoint(Circle c,Segment s){
    Line l=Line(s.p1,s.p2);
    int ko=intersect(c,s);
    if(ko==2) return crosspoint(c,l);
    vector<Point> ret;
    if(ko==0) return ret;
    ret=crosspoint(c,l);
    if(ret.size()==1) return ret;
    vector<Point> rret;
    //交点で挟める方を返す
    if(dot(s.p1-ret[0],s.p2-ret[0])<0)  rret.push_back(ret[0]);
    else                                rret.push_back(ret[1]);
    return rret;
}
//v
vector<Point> crosspoint(Circle c1,Circle c2){
    vector<Point> ret;
    int isec=intersect(c1,c2);
    if(isec==0 or isec==4) return ret;
    Real d=abs(c1.center-c2.center);
    Real a=acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d));
    Real t=atan2(c2.center.imag()-c1.center.imag(),c2.center.real()-c1.center.real());
    ret.push_back(c1.center+Point(cos(t+a)*c1.r,sin(t+a)*c1.r));
    ret.push_back(c1.center+Point(cos(t-a)*c1.r,sin(t-a)*c1.r));
    return ret;
}
 
//v
//点pから引いた円cの接線の接点を返す
vector<Point> tangent(Circle c,Point p){
    return crosspoint(c,Circle(p,sqrt(norm(c.center-p)-c.r*c.r)));
}
//v
//二円の共通接線，Lineの2点は接点を表す
vector<Line> tangent(Circle c1,Circle c2){
    vector<Line> ret;
    if(c1.r<c2.r) swap(c1,c2);
    Real g=norm(c1.center-c2.center);
    //中心が一致するならない
    if(eq(g,0)) return ret;
    Point u=(c2.center-c1.center)/sqrt(g);
    Point v=rotate(pi*0.5,u);
    for(int s:{-1,1}){
        Real h=(c1.r+s*c2.r)/sqrt(g);
        if(eq(1-h*h,0)){
            ret.push_back(Line(c1.center+u*c1.r,c1.center+(u+v)*c1.r));
        }
        else if(1-h*h>0){
            Point uu=u*h,vv=v*sqrt(1-h*h);
            ret.push_back(Line(c1.center+(uu+vv)*c1.r,c2.center-(uu+vv)*c2.r*s));
            ret.push_back(Line(c1.center+(uu-vv)*c1.r,c2.center-(uu-vv)*c2.r*s));
        }
    }
    return ret;
}
 
//v
//最小包含円を返す　計算量は期待値O(n)
Circle MinimumBoundingCircle(vector<Point> v){
    int n=v.size();
  
    //ランダムシャッフル．いぢわるされたくないもんだ
    mt19937 mt(time(0));
    shuffle(v.begin(),v.end(),mt);
    Circle ret(0,0);
    //2点で円を作る
    auto make_circle2=[&](Point a,Point b){
        return Circle((a+b)*0.5,dis(a,b)/2);
    };
    //3点で円を作る
    auto make_circle3=[&](Point A,Point B,Point C){
        Point cent=circumcenter(A,B,C);
        return Circle(cent,dis(cent,A));
    };
    auto isIn=[&](Point a){
        return dis(ret.center,a)<ret.r+EPS;
    };
 
    ret=make_circle2(v[0],v[1]);
    for(int i=2;i<n;i++){
        //v[i]が円に入っていないなら
        if(!isIn(v[i])){
            //円内にないなら点v[i]は必ず円周上に来る
            ret=make_circle2(v[0],v[i]);
            for(int j=1;j<i;j++){
                if(!isIn(v[j])){
                    //この時iとjが円周上を考える
                    ret=make_circle2(v[i],v[j]);
                    //最後の1点の決定
                    for(int k=0;k<j;k++){
                        if(!isIn(v[k])){
                            ret=make_circle3(v[i],v[j],v[k]);
                        }
                    }
                }
            }
        }
    }
    return ret;
}

// 繰り返し二乗法 
ll pow_mod(ll x, ll n){
    if(n==0) return 1;
    ll ret=pow_mod((x*x) % mod, n/2);
    if(n&1) ret=(ret*x)%mod;
    return ret;
}

//mod変えて何回かやったminをとるべきかもしれない
int chromatic_number(const vector<vector<bool>> &g){
    int n=(int)g.size();
    vector<int> es(n,0);
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)es[i]|=(g[i][j]<<j);
    vector<int> I(1<<n);I[0]=1;
    for(int S=1;S<(1<<n);S++){
        int v=__builtin_ctz(S);
        //S/{v}にvを別色で塗る+vをSの関与しない部分集合のどこかに属させる
        I[S]=I[S^(1<<v)]+I[(S^(1<<v))&(~es[v])];
    }
    int lw=0,hi=n;
    while(hi-lw>1){
        int mid=(lw+hi)/2;
        ll g=0;
        for(int S=0;S<(1<<n);S++){
            //f[S]=I[S]^k
            if((n-__builtin_popcount(S))&1) g-=pow_mod(I[S],mid);
            else                            g+=pow_mod(I[S],mid);
            g=(g%mod+mod)%mod;
        }
        if(g!=0)    hi=mid;
        else        lw=mid;
    }
    return hi;
}

int n;
void solve(){
    vector<vector<Segment>> ls(n);
    rep(i,n){
        int m;cin>>m;
        Point pre;
        cin>>pre;
        rep(j,m-1){
            Point p;cin>>p;
            ls[i].push_back(Segment(p,pre));
            pre=p;
        }
    }
    vector<vector<bool>> g(n,vector<bool>(n));
    rep(i,n){
        for(int j=i+1;j<n;j++){
            bool f=false;
            for(auto a:ls[i]){
                for(auto b:ls[j]){
                    if(intersect(a,b)) f=true;
                }
            }
            g[i][j]=f;
            g[j][i]=f;
        }
    }
    cout<<chromatic_number(g)<<endl;
}

signed main(){
    cin.tie(0);
    ios::sync_with_stdio(0);

    while(cin>>n,n){
        solve();
    }
    return 0;
}
accuracy: 1
time_ms: 4970
memory_kb: 19724
final_rank: 16