Problem:
最終防衛線
たびたび未確認生物に侵略されるようになった某国では重要施設を新型防衛兵器で保護することにした。
この兵器は多角形の領域に特殊なガスを充満させることで未確認生物にダメージを与えることができる。未確認生物が侵攻する途中でガスの濃度が変わると、濃度の差の絶対値のダメージを与える。ガスの濃度が同じ領域を動いているときはダメージは一切発生しない。
現在の技術ではガスの濃度は一定にしかできないので、某国は新型防衛兵器を複数投入することにした。全ての兵器でガスの濃度は同じであり、複数の兵器の領域に含まれる部分は濃度が足し合わされる。
未確認生物の出現地点と重要施設の位置を元に、未確認生物が重要施設まで侵攻する時に受けるダメージの最小値を求めて欲しい。
ただし、未確認生物の侵攻ルートに多角形の頂点や他の多角形との交点は含まれないものとする。また、多角形の辺上を辺にそって侵攻することはない。
Input
入力は以下の形式で与えられる。
多角形の数
多角形の頂点数
x座標 y座標
x座標 y座標
...
多角形の頂点数
x座標 y座標
x座標 y座標
...
出現地点と重要施設のデータの数
出現位置のx座標 出現位置のy座標 重要施設のx座標 重要施設のy座標
出現位置のx座標 出現位置のy座標 重要施設のx座標 重要施設のy座標
...
Constraints
入力に含まれる座標は絶対値が1,000以下の整数
多角形の数は1以上5以下
多角形の頂点数は3以上5以下
出現地点と重要施設のデータの数は1以上100以下
多角形は与えられた頂点を順につないでできる多角形を指し、自己交差はない
出現地点と重要施設は多角形の頂点及び辺上にあることはない
Output
出現地点と重要施設の組ごとに未確認生物が重要施設まで侵攻する際に受けるダメージの最小値を1行ずつ出力せよ。
Sample Input 1
2
4
0 4
1 1
3 1
4 4
3
6 0
10 0
8 7
1
2 3 9 1
Output for the Sample Input 1
2
1つ目の多角形を出る時に1ダメージを与え、2つ目の多角形に入る時に1ダメージを与える。
Sample Input 2
1
4
0 0
10 0
10 10
0 10
2
15 5 5 5
5 5 15 5
Output for the Sample Input 2
1
1
1つ目のデータでは未確認生物が多角形に入る時にダメージが発生する。
2つ目のデータでは未確認生物が多角形から出る時にダメージが発生する。
Sample Input 3
2
4
0 0
10 0
10 10
0 10
4
10 0
20 0
20 10
10 10
1
5 5 15 5
Output for the Sample Input 3
0
未確認生物が1つ目の多角形から2つ目の多角形に動くルートを取ると濃度が変わることがないのでダメージを与えることができない。
Sample Input 4
2
3
0 0
10 0
5 10
3
5 0
15 0
10 10
1
5 5 10 5
Output for the Sample Input 4
2
出現地点の領域と重要施設の領域は点で接しているがこの点を通って侵攻することはない。
Sample Input 5
2
4
0 0
10 0
10 10
0 10
4
0 0
10 0
10 10
0 10
2
15 5 5 5
5 5 15 5
Output for the Sample Input 5
2
2

Submitted solutions:

code_snippet: #include <algorithm>
#include <assert.h>
#include <bitset>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <valarray>
#include <vector>
using namespace std;
typedef long long int ll;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define REP(i, x) for (int i = 0; i < (int)(x); i++)
#define REPS(i, x) for (int i = 1; i <= (int)(x); i++)
#define RREP(i, x) for (int i = ((int)(x)-1); i >= 0; i--)
#define RREPS(i, x) for (int i = ((int)(x)); i > 0; i--)
#define FOR(i, c)                                                              \
  for (__typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define RFOR(i, c)                                                             \
  for (__typeof((c).rbegin()) i = (c).rbegin(); i != (c).rend(); i++)
#define ALL(container) (container).begin(), (container).end()
#define RALL(container) (container).rbegin(), (container).rend()
#define SZ(container) ((int)container.size())
#define mp(a, b) make_pair(a, b)
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
template <class T> bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &t) {
  os << "[";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "]";
  return os;
}
template <class T> ostream &operator<<(ostream &os, const set<T> &t) {
  os << "{";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "}";
  return os;
}
template <class S, class T>
ostream &operator<<(ostream &os, const pair<S, T> &t) {
  return os << "(" << t.first << "," << t.second << ")";
}
template <class S, class T>
pair<S, T> operator+(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first + t.first, s.second + t.second);
}
template <class S, class T>
pair<S, T> operator-(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first - t.first, s.second - t.second);
}
struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x])
        swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};
namespace geom {
#define X real()
#define Y imag()
#define at(i) ((*this)[i])
#define SELF (*this)
enum { TRUE = 1, FALSE = 0, BORDER = -1 };
typedef int BOOL;
typedef double R;
const R INF = 1e8;
const R EPS = 1e-8;
const R PI = 3.1415926535897932384626;
inline int sig(const R &x) { return (abs(x) < EPS ? 0 : x > 0 ? 1 : -1); }
inline BOOL less(const R &x, const R &y) { return sig(x - y) ? x < y : BORDER; }
typedef complex<R> P;
inline R norm(const P &p) { return p.X * p.X + p.Y * p.Y; }
inline R inp(const P &a, const P &b) { return (conj(a) * b).X; }
inline R outp(const P &a, const P &b) { return (conj(a) * b).Y; }
inline P unit(const P &p) { return p / abs(p); }
inline P proj(const P &s, const P &t) { return t * inp(s, t) / norm(t); }
inline int ccw(const P &s, const P &t, const P &p, int adv = 0) {
  int res = sig(outp(t - s, p - s));
  if (res || !adv)
    return res;
  if (sig(inp(t - s, p - s)) < 0)
    return -2;
  if (sig(inp(s - t, p - t)) < 0)
    return 2;
  return 0;
}
struct L : public vector<P> {
  L(const P &p1, const P &p2) {
    this->push_back(p1);
    this->push_back(p2);
  }
  L() {}
  P dir() const { return at(1) - at(0); }
  BOOL online(const P &p) const { return !sig(outp(p - at(0), dir())); }
};
struct S : public L {
  S(const P &p1, const P &p2) : L(p1, p2) {}
  S() {}
  BOOL online(const P &p) const {
    if (!sig(norm(p - at(0))) || !sig(norm(p - at(1))))
      return BORDER;
    return !sig(abs(at(0) - p) + abs(at(1) - p) - abs(at(0) - at(1)));
  }
};
struct C : public P {
  C() {}
  C(const P &p, const R r) : P(p), r(r) {}
  R r;
  BOOL inside(const P &p) const { return less(norm(p - SELF), r * r); }
};
struct F : public C {
  R s, t;
  F(const C &c, R ss, R tt) : C(c), s(ss), t(tt) {
    if (PI < s)
      s -= 2 * PI;
    if (PI < t)
      t -= 2 * PI;
  }
  BOOL inside(const P &p) const {
    P v = p - SELF;
    if (!sig(norm(v)))
      return BORDER;
    R a = arg(v);
    if (t < s) {
      if ((!less(s, a) && !less(a, t)) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    } else {
      if (!less(s, a) || !less(a, t) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    }
  }
};
P crosspoint(const L &l, const L &m);
struct G : public vector<P> {
  G(size_type size = 0) : vector(size) {}
  S edge(int i) const { return S(at(i), at(i + 1 == size() ? 0 : i + 1)); }
  BOOL contains(const P &p) const {
    R sum = .0;
    REP(i, size()) {
      if (S(at(i), at((i + 1) % size())).online(p))
        return BORDER;
      sum += arg((at(i) - p) / (at((i + 1) % size()) - p));
    }
    return !!sig(sum);
  }
  R area() const {
    R sum = 0;
    REP(i, size()) sum += outp(at(i), at((i + 1) % size()));
    return abs(sum / 2.);
  }
  G convex_hull(bool online = false) {
    if (size() < 2)
      return *this;
    sort(ALL(*this));
    G r;
    r.resize((int)size() * 2);
    int k = 0;
    for (int i = 0; i < size(); r[k++] = at(i++))
      while (k > 1 && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    int t = k;
    for (int i = (int)size() - 1; i >= 0; r[k++] = at(i--))
      while (k > t && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    r.resize(k - 1);
    return r;
  }
  G cut(const L &l) const {
    G g;
    REP(i, size()) {
      const S &s = edge(i);
      if (ccw(l[0], l[1], s[0], 0) >= 0)
        g.push_back(s[0]);
      if (ccw(l[0], l[1], s[0], 0) * ccw(l[0], l[1], s[1], 0) < 0)
        g.push_back(crosspoint(s, l));
    }
    return g;
  }
  G Voronoi(const vector<P> &p, const int t) const {
    G g = *this;
    REP(i, p.size()) if (i != t) {
      const P m = (p[t] + p[i]) * 0.5;
      g = g.cut(L(m, m + (p[i] - p[t]) * P(0, 1)));
    }
    return g;
  }
};
inline P proj(const P &s, const L &t) {
  return t[0] + proj(s - t[0], t[1] - t[0]);
}
inline P reflect(const P &s, const L &t) { return 2. * proj(s, t) - s; }
inline S reflect(const S &s, const L &t) {
  return S(reflect(s[0], t), reflect(s[1], t));
}
BOOL intersect(const S &s, const S &t) {
  const int p = ccw(t[0], t[1], s[0], 1) * ccw(t[0], t[1], s[1], 1);
  const int q = ccw(s[0], s[1], t[0], 1) * ccw(s[0], s[1], t[1], 1);
  return (p > 0 || q > 0) ? FALSE : (!p || !q) ? BORDER : TRUE;
}
BOOL intersect(const S &s, const L &l) {
  if (l.online(s[0]) || l.online(s[1]))
    return BORDER;
  return (sig(outp(l.dir(), s[0] - l[0])) * sig(outp(l.dir(), s[1] - l[0])) <=
          0);
}
R dist2(const L &l, const P &p) {
  return norm(outp(l.dir(), p - l[0])) / norm(l.dir());
}
R dist2(const S &s, const P &p) {
  if (inp(p - s[0], s.dir()) < EPS)
    return norm(p - s[0]);
  if (inp(p - s[1], -s.dir()) < EPS)
    return norm(p - s[1]);
  return dist2((const L &)s, p);
}
R dist2(const S &s, const L &l) {
  return intersect(s, l) ? .0 : min(dist2(l, s[0]), dist2(l, s[1]));
}
R dist2(const S &s, const S &t) {
  return intersect(s, t) ? .0
                         : min(min(dist2(s, t[0]), dist2(t, s[0])),
                               min(dist2(s, t[1]), dist2(t, s[1])));
}
template <class T> R dist2(const G &g, const T &t) {
  R res = INF;
  REP(i, g.size()) res = min(res, dist2(g.edge(i), t));
  return res;
}
template <class S, class T> R dist(const S &s, const T &t) {
  return sqrt(dist2(s, t));
}
inline BOOL intersect(const C &a, const C &b) {
  return less((a.r - b.r) * (a.r - b.r), norm(a - b)) +
         less(norm(a - b), (a.r + b.r) * (a.r + b.r)) - 1;
}
inline BOOL intersect(const C &c, const L &l) {
  return less(dist2(l, c), c.r * c.r);
}
inline BOOL intersect(const C &c, const S &s) {
  int d = less(dist2(s, c), c.r * c.r);
  if (d != TRUE)
    return d;
  int p = c.inside(s[0]), q = c.inside(s[1]);
  return (p < 0 || q < 0) ? BORDER : p & q;
}
inline S crosspoint(const C &c1, const C &c2) {
  if (!intersect(c1, c2))
    return S();
  R d = abs(c1 - c2);
  R x = (c1.r * c1.r - c2.r * c2.r + d * d) / (2 * d);
  R h = sqrt(c1.r * c1.r - x * x);
  P u = unit(c2 - c1);
  return S(c1 + u * x + u * P(0, -1) * h, c1 + u * x + u * P(0, 1) * h);
}
inline P crosspoint(const L &l, const L &m) {
  R A = outp(l.dir(), m.dir()), B = outp(l.dir(), l[1] - m[0]);
  if (!sig(abs(A)) && !sig(abs(B)))
    return m[0];
  if (abs(A) < EPS)
    assert(false);
  return m[0] + B / A * (m[1] - m[0]);
}
inline S crosspoint(const C &c, const L &l) {
  R d2 = dist2(l, c);
  if (c.r * c.r + EPS < d2)
    return S();
  P m = proj(c, l);
  P u = unit(l[1] - l[0]);
  R d = sqrt(c.r * c.r - d2);
  return S(m + u * d, m - u * d);
}
inline vector<P> crosspoint(const C &c, const S &s) {
  vector<P> res = crosspoint(c, (const L &)s);
  RREP(i, res.size()) {
    if (inp(res[i] - s[0], s.dir()) * inp(res[i] - s[1], -s.dir()) < EPS)
      res.erase(res.begin() + i);
  }
  return res;
}
inline R commonarea(const C &a, const C &b) {
  if (less(norm(a - b), (a.r - b.r) * (a.r - b.r)) == TRUE)
    return min(a.r * a.r, b.r * b.r) * PI;
  if (less((a.r + b.r) * (a.r + b.r), norm(a - b)) == TRUE)
    return .0;
  double d = abs(a - b);
  double rc = (d * d + a.r * a.r - b.r * b.r) / (2 * d);
  double theta = acos(rc / a.r);
  double phi = acos((d - rc) / b.r);
  return a.r * a.r * theta + b.r * b.r * phi - d * a.r * sin(theta);
}
vector<L> CommonTangent(C c1, C c2) {
  if (c1.r > c2.r)
    swap(c1, c2);
  double d = abs(c1 - c2);
  vector<L> res;
  if (d < EPS)
    return res;
  if (d + EPS > c1.r + c2.r) {
    P crs = (c1 * c2.r + c2 * c1.r) / (c1.r + c2.r);
    double rad = asin(c1.r / abs(crs - c1));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., rad)));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., -rad)));
  }
  if (c1.r + d + EPS > c2.r) {
    double rad = 0.5 * PI + asin((c2.r - c1.r) / d);
    P v = unit(c2 - c1) * polar(1., rad);
    if (c1.r + d - EPS < c2.r) {
      res.push_back(L(c1 + v * c1.r, c1 + v * c1.r + (c1 - c2) * P(0, 1)));
    } else {
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
      v = 2. * proj(v, c2 - c1) - v;
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
    }
  }
  return res;
}
struct min_ball {
  P center;
  R radius2;
  min_ball(const vector<P> &p) { FOR(it, p) ps.push_back(*it); }
  min_ball &compile() {
    m = 0;
    center = P(0, 0);
    radius2 = -1;
    make_ball(ps.end());
    return *this;
  }

private:
  list<P> ps;
  list<P>::iterator supp_end;
  int m;
  P v[3], c[3];
  R z[3], r[3];
  void pop() { --m; }
  void push(const P &p) {
    if (m == 0) {
      c[0] = p;
      r[0] = 0;
    } else {
      R e = norm(p - c[m - 1]) - r[m - 1];
      P delta = p - c[0];
      v[m] = p - c[0];
      for (int i = 1; i < m; ++i)
        v[m] -= v[i] * inp(v[i], delta) / z[i];
      z[m] = inp(v[m], v[m]);
      c[m] = c[m - 1] + e * v[m] / z[m] * .5;
      r[m] = r[m - 1] + e * e / z[m] * .25;
    }
    center = c[m];
    radius2 = r[m];
    ++m;
  }
  void make_ball(list<P>::iterator i) {
    supp_end = ps.begin();
    if (m == 3)
      return;
    for (list<P>::iterator k = ps.begin(); k != i;) {
      list<P>::iterator j = k++;
      if (norm(*j - center) > radius2) {
        push(*j);
        make_ball(j);
        pop();
        move_to_front(j);
      }
    }
  }
  void move_to_front(list<P>::iterator j) {
    if (supp_end == j)
      ++supp_end;
    ps.splice(ps.begin(), ps, j);
  }
};
struct Arrangement {
  struct AEdge {
    int u, v, t;
    R cost;
    AEdge(int u = 0, int v = 0, int t = 0, R cost = 0)
        : u(u), v(v), t(t), cost(cost) {}
  };
  typedef vector<vector<AEdge>> AGraph;
  vector<P> p;
  AGraph g;
  Arrangement() {}
  Arrangement(vector<S> seg) {
    int m = seg.size();
    REP(i, m) {
      p.push_back(seg[i][0]);
      p.push_back(seg[i][1]);
      REP(j, i)
      if (sig(outp(seg[i].dir(), seg[j].dir())) &&
          intersect(seg[i], seg[j]) == TRUE)
          p.push_back(crosspoint(seg[i], seg[j]));
    }
    sort(ALL(p));
    UNIQUE(p);
    int n = p.size();
    g.resize(n);
    REP(i, m) {
      S &s = seg[i];
      vector<pair<R, int>> ps;
      REP(j, n) if (s.online(p[j])) ps.emplace_back(norm(p[j] - s[0]), j);
      sort(ALL(ps));
      REP(j, (int)ps.size() - 1) {
        const int u = ps[j].second;
        const int v = ps[j + 1].second;
        g[u].emplace_back(u, v, 0, abs(p[u] - p[v]));
        g[v].emplace_back(v, u, 0, abs(p[u] - p[v]));
      }
    }
  }
  int getIdx(P q) {
    auto it = lower_bound(ALL(p), q);
    if (it == p.end() || *it != q)
      return -1;
    return it - p.begin();
  }
};
struct DualGraph {
  struct DEdge {
    int u, v, f, l;
    R a;
    DEdge(int u, int v, R a) : u(u), v(v), f(0), l(0) {
      while (PI < a)
        a -= 2 * PI;
      while (a < -PI)
        a += 2 * PI;
      this->a = a;
    }
    bool operator==(const DEdge &opp) const { return v == opp.v; }
    bool operator<(const DEdge &opp) const { return a > opp.a; }
    bool operator<(const R &opp) const { return a > opp; }
    friend ostream &operator<<(ostream &os, const DEdge &t) {
      return os << "(" << t.u << "," << t.v << "," << t.a * 180 / PI << ")";
    }
  };
  int n;
  vector<P> p;
  vector<vector<DEdge>> g;
  UnionFind uf;
  DualGraph(const vector<P> &p)
      : p(p), g(p.size()), n(p.size()), uf(p.size()) {}
  void add_edge(const int s, const int t) {
    R a = arg(p[t] - p[s]);
    g[s].emplace_back(s, t, a);
    g[t].emplace_back(t, s, a > 0 ? a - PI : a + PI);
    uf.unionSet(s, t);
  }
  vector<G> poly;
  void add_polygon(int s, int t, R a) {
    auto e = lower_bound(ALL(g[s]), a - EPS);
    if (e == g[s].end())
      e = g[s].begin();
    if (e->f)
      return;
    e->f = 1;
    e->l = t;
    poly[t].push_back(p[s]);
    add_polygon(e->v, t, e->a > 0 ? e->a - PI : e->a + PI);
  }
  void toSpanningTree() {
    vector<pair<R, pii>> e;
    REP(i, n)
    REP(j, i) if (!uf.findSet(i, j))
        e.emplace_back(norm(p[i] - p[j]), pii(i, j));
    sort(ALL(e));
    REP(ii, e.size()) {
      const int i = e[ii].second.first;
      const int j = e[ii].second.second;
      if (uf.findSet(i, j))
        continue;
      const S s(p[i], p[j]);
      if ([&]() {
            REP(k, n) if (i != k && j != k) {
              FOR(it, g[k])
              if (intersect(s, S(p[k], p[it->v])) == TRUE)
                return 0;
            }
            return 1;
          }())
        add_edge(i, j);
    }
  }
  vector<vi> dual() {
    toSpanningTree();
    REP(i, n) {
      sort(ALL(g[i]));
      UNIQUE(g[i]);
    }
    int s = min_element(ALL(p)) - p.begin();
    poly.emplace_back();
    add_polygon(s, poly.size() - 1, -PI * .5);
    REP(i, n) REP(j, g[i].size()) if (!g[i][j].f) {
      poly.emplace_back();
      add_polygon(i, poly.size() - 1, g[i][j].a + 2. * EPS);
    }
    int m = poly.size();
    vector<vi> dg(m, vi(m, 1 << 20));
    REP(i, m) dg[i][i] = 0;
    vector<unordered_map<int, int>> rev(n);
    REP(i, n) REP(j, g[i].size()) {
      int u = i, v = g[i][j].v, l = g[i][j].l;
      if (u > v)
        swap(u, v);
      if (rev[u].find(v) != rev[u].end()) {
        if (l != rev[u][v]) {
          dg[l][rev[u][v]] = 1;
          dg[rev[u][v]][l] = 1;
        }
      } else
        rev[u][v] = l;
    }
    return dg;
  }
};
#undef SELF
#undef at
} // namespace geom
using namespace geom;
namespace std {
bool operator<(const P &a, const P &b) {
  return sig(a.X - b.X) ? a.X < b.X : a.Y + EPS < b.Y;
}
bool operator==(const P &a, const P &b) { return !sig(norm(a - b)); }
istream &operator>>(istream &is, P &p) {
  R x, y;
  is >> x >> y;
  p = P(x, y);
  return is;
}
istream &operator>>(istream &is, L &l) {
  l.resize(2);
  return is >> l[0] >> l[1];
}
istream &operator>>(istream &is, C &c) { return is >> (P &)c >> c.r; }
const double B = 500;
const double Z = 1;
ostream &operator<<(ostream &os, const C &c) {
  return os << "circle(" << B + Z * (c.X) << ", " << 1000 - B - Z * (c.Y)
            << ", " << Z * (c.r) << ")";
}
ostream &operator<<(ostream &os, const P &p) { return os << C(p, 2.); }
ostream &operator<<(ostream &os, const S &s) {
  return os << "line(" << B + Z * (s[0].X) << ", " << 1000 - B - Z * (s[0].Y)
            << ", " << B + Z * (s[1].X) << ", " << 1000 - B - Z * (s[1].Y)
            << ")";
}
ostream &operator<<(ostream &os, const G &g) {
  REP(i, g.size()) cout << g.edge(i) << endl;
  return os;
}
} // namespace std
int n;
int main() {
  ios::sync_with_stdio(false);
  while (cin >> n) {
    vector<G> poly(n);
    vector<S> s;
    REP(i, n) {
      int x;
      cin >> x;
      poly[i].resize(x);
      REP(j, x) cin >> poly[i][j];
      REP(j, x) s.push_back(poly[i].edge(j));
    }
    Arrangement arg(s);
    DualGraph dg(arg.p);
    REP(i, arg.g.size()) FOR(e, arg.g[i]) dg.add_edge(e->u, e->v);
    vector<vi> g = dg.dual();
    const auto &area = dg.poly;
    int m = area.size();
    vi level(m, -1);
    level[0] = 0;
    REPS(i, m - 1) {
      level[i] = count_if(ALL(poly), [&](const G &arg) {
        REP(j, area[i].size()) {
          const S &s = area[i].edge(j);
          const P p = (s[0] + s[1]) * .5;
          if (!arg.contains(p))
            return 0;
        }
        return 1;
      });
      if (level[i] == -1)
        assert(false);
    }
    REP(i, m)
    REP(j, i) if (g[i][j] == 1) g[i][j] = g[j][i] = abs(level[i] - level[j]);
    REP(k, m) REP(i, m) REP(j, m) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
    int q;
    cin >> q;
    REP(i, q) {
      P s, t;
      cin >> s >> t;
      int su = 0, tu = 0;
      REPS(j, m - 1) {
        if (area[j].contains(s))
          su = j;
        if (area[j].contains(t))
          tu = j;
      }
      cout << g[su][tu] << endl;
    }
  }
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4044
final_rank: 3

code_snippet: #include <algorithm>
#include <assert.h>
#include <bitset>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <valarray>
#include <vector>
using namespace std;
typedef long long int ll;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define REP(i, x) for (int i = 0; i < (int)(x); i++)
#define REPS(i, x) for (int i = 1; i <= (int)(x); i++)
#define RREP(i, x) for (int i = ((int)(x)-1); i >= 0; i--)
#define RREPS(i, x) for (int i = ((int)(x)); i > 0; i--)
#define FOR(i, c)                                                              \
  for (__typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define RFOR(i, c)                                                             \
  for (__typeof((c).rbegin()) i = (c).rbegin(); i != (c).rend(); i++)
#define ALL(container) (container).begin(), (container).end()
#define RALL(container) (container).rbegin(), (container).rend()
#define SZ(container) ((int)container.size())
#define mp(a, b) make_pair(a, b)
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
template <class T> bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &t) {
  os << "[";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "]";
  return os;
}
template <class T> ostream &operator<<(ostream &os, const set<T> &t) {
  os << "{";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "}";
  return os;
}
template <class S, class T>
ostream &operator<<(ostream &os, const pair<S, T> &t) {
  return os << "(" << t.first << "," << t.second << ")";
}
template <class S, class T>
pair<S, T> operator+(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first + t.first, s.second + t.second);
}
template <class S, class T>
pair<S, T> operator-(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first - t.first, s.second - t.second);
}
struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x])
        swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};
namespace geom {
#define X real()
#define Y imag()
#define at(i) ((*this)[i])
#define SELF (*this)
enum { TRUE = 1, FALSE = 0, BORDER = -1 };
typedef int BOOL;
typedef double R;
const R INF = 1e8;
const R EPS = 1e-8;
const R PI = 3.1415926535897932384626;
inline int sig(const R &x) { return (abs(x) < EPS ? 0 : x > 0 ? 1 : -1); }
inline BOOL less(const R &x, const R &y) { return sig(x - y) ? x < y : BORDER; }
typedef complex<R> P;
inline R norm(const P &p) { return p.X * p.X + p.Y * p.Y; }
inline R inp(const P &a, const P &b) { return (conj(a) * b).X; }
inline R outp(const P &a, const P &b) { return (conj(a) * b).Y; }
inline P unit(const P &p) { return p / abs(p); }
inline P proj(const P &s, const P &t) { return t * inp(s, t) / norm(t); }
inline int ccw(const P &s, const P &t, const P &p, int adv = 0) {
  int res = sig(outp(t - s, p - s));
  if (res || !adv)
    return res;
  if (sig(inp(t - s, p - s)) < 0)
    return -2;
  if (sig(inp(s - t, p - t)) < 0)
    return 2;
  return 0;
}
struct L : public vector<P> {
  L(const P &p1, const P &p2) {
    this->push_back(p1);
    this->push_back(p2);
  }
  L() {}
  P dir() const { return at(1) - at(0); }
  BOOL online(const P &p) const { return !sig(outp(p - at(0), dir())); }
};
struct S : public L {
  S(const P &p1, const P &p2) : L(p1, p2) {}
  S() {}
  BOOL online(const P &p) const {
    if (!sig(norm(p - at(0))) || !sig(norm(p - at(1))))
      return BORDER;
    return !sig(abs(at(0) - p) + abs(at(1) - p) - abs(at(0) - at(1)));
  }
};
struct C : public P {
  C() {}
  C(const P &p, const R r) : P(p), r(r) {}
  R r;
  BOOL inside(const P &p) const { return less(norm(p - SELF), r * r); }
};
struct F : public C {
  R s, t;
  F(const C &c, R ss, R tt) : C(c), s(ss), t(tt) {
    if (PI < s)
      s -= 2 * PI;
    if (PI < t)
      t -= 2 * PI;
  }
  BOOL inside(const P &p) const {
    P v = p - SELF;
    if (!sig(norm(v)))
      return BORDER;
    R a = arg(v);
    if (t < s) {
      if ((!less(s, a) && !less(a, t)) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    } else {
      if (!less(s, a) || !less(a, t) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    }
  }
};
P crosspoint(const L &l, const L &m);
struct G : public vector<P> {
  G(size_type size = 0) : vector(size) {}
  S edge(int i) const { return S(at(i), at(i + 1 == size() ? 0 : i + 1)); }
  BOOL contains(const P &p) const {
    R sum = .0;
    REP(i, size()) {
      if (S(at(i), at((i + 1) % size())).online(p))
        return BORDER;
      sum += arg((at(i) - p) / (at((i + 1) % size()) - p));
    }
    return !!sig(sum);
  }
  R area() const {
    R sum = 0;
    REP(i, size()) sum += outp(at(i), at((i + 1) % size()));
    return abs(sum / 2.);
  }
  G convex_hull(bool online = false) {
    if (size() < 2)
      return *this;
    sort(ALL(*this));
    G r;
    r.resize((int)size() * 2);
    int k = 0;
    for (int i = 0; i < size(); r[k++] = at(i++))
      while (k > 1 && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    int t = k;
    for (int i = (int)size() - 1; i >= 0; r[k++] = at(i--))
      while (k > t && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    r.resize(k - 1);
    return r;
  }
  G cut(const L &l) const {
    G g;
    REP(i, size()) {
      const S &s = edge(i);
      if (ccw(l[0], l[1], s[0], 0) >= 0)
        g.push_back(s[0]);
      if (ccw(l[0], l[1], s[0], 0) * ccw(l[0], l[1], s[1], 0) < 0)
        g.push_back(crosspoint(s, l));
    }
    return g;
  }
  G Voronoi(const vector<P> &p, const int t) const {
    G g = *this;
    REP(i, p.size()) if (i != t) {
      const P m = (p[t] + p[i]) * 0.5;
      g = g.cut(L(m, m + (p[i] - p[t]) * P(0, 1)));
    }
    return g;
  }
};
inline P proj(const P &s, const L &t) {
  return t[0] + proj(s - t[0], t[1] - t[0]);
}
inline P reflect(const P &s, const L &t) { return 2. * proj(s, t) - s; }
inline S reflect(const S &s, const L &t) {
  return S(reflect(s[0], t), reflect(s[1], t));
}
BOOL intersect(const S &s, const S &t) {
  const int p = ccw(t[0], t[1], s[0], 1) * ccw(t[0], t[1], s[1], 1);
  const int q = ccw(s[0], s[1], t[0], 1) * ccw(s[0], s[1], t[1], 1);
  return (p > 0 || q > 0) ? FALSE : (!p || !q) ? BORDER : TRUE;
}
BOOL intersect(const S &s, const L &l) {
  if (l.online(s[0]) || l.online(s[1]))
    return BORDER;
  return (sig(outp(l.dir(), s[0] - l[0])) * sig(outp(l.dir(), s[1] - l[0])) <=
          0);
}
R dist2(const L &l, const P &p) {
  return norm(outp(l.dir(), p - l[0])) / norm(l.dir());
}
R dist2(const S &s, const P &p) {
  if (inp(p - s[0], s.dir()) < EPS)
    return norm(p - s[0]);
  if (inp(p - s[1], -s.dir()) < EPS)
    return norm(p - s[1]);
  return dist2((const L &)s, p);
}
R dist2(const S &s, const L &l) {
  return intersect(s, l) ? .0 : min(dist2(l, s[0]), dist2(l, s[1]));
}
R dist2(const S &s, const S &t) {
  return intersect(s, t) ? .0
                         : min(min(dist2(s, t[0]), dist2(t, s[0])),
                               min(dist2(s, t[1]), dist2(t, s[1])));
}
template <class T> R dist2(const G &g, const T &t) {
  R res = INF;
  REP(i, g.size()) res = min(res, dist2(g.edge(i), t));
  return res;
}
template <class S, class T> R dist(const S &s, const T &t) {
  return sqrt(dist2(s, t));
}
inline BOOL intersect(const C &a, const C &b) {
  return less((a.r - b.r) * (a.r - b.r), norm(a - b)) +
         less(norm(a - b), (a.r + b.r) * (a.r + b.r)) - 1;
}
inline BOOL intersect(const C &c, const L &l) {
  return less(dist2(l, c), c.r * c.r);
}
inline BOOL intersect(const C &c, const S &s) {
  int d = less(dist2(s, c), c.r * c.r);
  if (d != TRUE)
    return d;
  int p = c.inside(s[0]), q = c.inside(s[1]);
  return (p < 0 || q < 0) ? BORDER : p & q;
}
inline S crosspoint(const C &c1, const C &c2) {
  if (!intersect(c1, c2))
    return S();
  R d = abs(c1 - c2);
  R x = (c1.r * c1.r - c2.r * c2.r + d * d) / (2 * d);
  R h = sqrt(c1.r * c1.r - x * x);
  P u = unit(c2 - c1);
  return S(c1 + u * x + u * P(0, -1) * h, c1 + u * x + u * P(0, 1) * h);
}
inline P crosspoint(const L &l, const L &m) {
  R A = outp(l.dir(), m.dir()), B = outp(l.dir(), l[1] - m[0]);
  if (!sig(abs(A)) && !sig(abs(B)))
    return m[0];
  if (abs(A) < EPS)
    assert(false);
  return m[0] + B / A * (m[1] - m[0]);
}
inline S crosspoint(const C &c, const L &l) {
  R d2 = dist2(l, c);
  if (c.r * c.r + EPS < d2)
    return S();
  P m = proj(c, l);
  P u = unit(l[1] - l[0]);
  R d = sqrt(c.r * c.r - d2);
  return S(m + u * d, m - u * d);
}
inline vector<P> crosspoint(const C &c, const S &s) {
  vector<P> res = crosspoint(c, (const L &)s);
  RREP(i, res.size()) {
    if (inp(res[i] - s[0], s.dir()) * inp(res[i] - s[1], -s.dir()) < EPS)
      res.erase(res.begin() + i);
  }
  return res;
}
inline R commonarea(const C &a, const C &b) {
  if (less(norm(a - b), (a.r - b.r) * (a.r - b.r)) == TRUE)
    return min(a.r * a.r, b.r * b.r) * PI;
  if (less((a.r + b.r) * (a.r + b.r), norm(a - b)) == TRUE)
    return .0;
  double d = abs(a - b);
  double rc = (d * d + a.r * a.r - b.r * b.r) / (2 * d);
  double theta = acos(rc / a.r);
  double phi = acos((d - rc) / b.r);
  return a.r * a.r * theta + b.r * b.r * phi - d * a.r * sin(theta);
}
vector<L> CommonTangent(C c1, C c2) {
  if (c1.r > c2.r)
    swap(c1, c2);
  double d = abs(c1 - c2);
  vector<L> res;
  if (d < EPS)
    return res;
  if (d + EPS > c1.r + c2.r) {
    P crs = (c1 * c2.r + c2 * c1.r) / (c1.r + c2.r);
    double rad = asin(c1.r / abs(crs - c1));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., rad)));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., -rad)));
  }
  if (c1.r + d + EPS > c2.r) {
    double rad = 0.5 * PI + asin((c2.r - c1.r) / d);
    P v = unit(c2 - c1) * polar(1., rad);
    if (c1.r + d - EPS < c2.r) {
      res.push_back(L(c1 + v * c1.r, c1 + v * c1.r + (c1 - c2) * P(0, 1)));
    } else {
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
      v = 2. * proj(v, c2 - c1) - v;
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
    }
  }
  return res;
}
struct min_ball {
  P center;
  R radius2;
  min_ball(const vector<P> &p) { FOR(it, p) ps.push_back(*it); }
  min_ball &compile() {
    m = 0;
    center = P(0, 0);
    radius2 = -1;
    make_ball(ps.end());
    return *this;
  }

private:
  list<P> ps;
  list<P>::iterator supp_end;
  int m;
  P v[3], c[3];
  R z[3], r[3];
  void pop() { --m; }
  void push(const P &p) {
    if (m == 0) {
      c[0] = p;
      r[0] = 0;
    } else {
      R e = norm(p - c[m - 1]) - r[m - 1];
      P delta = p - c[0];
      v[m] = p - c[0];
      for (int i = 1; i < m; ++i)
        v[m] -= v[i] * inp(v[i], delta) / z[i];
      z[m] = inp(v[m], v[m]);
      c[m] = c[m - 1] + e * v[m] / z[m] * .5;
      r[m] = r[m - 1] + e * e / z[m] * .25;
    }
    center = c[m];
    radius2 = r[m];
    ++m;
  }
  void make_ball(list<P>::iterator i) {
    supp_end = ps.begin();
    if (m == 3)
      return;
    for (list<P>::iterator k = ps.begin(); k != i;) {
      list<P>::iterator j = k++;
      if (norm(*j - center) > radius2) {
        push(*j);
        make_ball(j);
        pop();
        move_to_front(j);
      }
    }
  }
  void move_to_front(list<P>::iterator j) {
    if (supp_end == j)
      ++supp_end;
    ps.splice(ps.begin(), ps, j);
  }
};
struct Arrangement {
  struct AEdge {
    int u, v, t;
    R cost;
    AEdge(int u = 0, int v = 0, int t = 0, R cost = 0)
        : u(u), v(v), t(t), cost(cost) {}
  };
  typedef vector<vector<AEdge>> AGraph;
  vector<P> p;
  AGraph g;
  Arrangement() {}
  Arrangement(vector<S> seg) {
    int m = seg.size();
    REP(i, m) {
      p.push_back(seg[i][0]);
      p.push_back(seg[i][1]);
      REP(j, i)
      if (sig(outp(seg[i].dir(), seg[j].dir())) &&
          intersect(seg[i], seg[j]) == TRUE)
          p.push_back(crosspoint(seg[i], seg[j]));
    }
    sort(ALL(p));
    UNIQUE(p);
    int n = p.size();
    g.resize(n);
    REP(i, m) {
      S &s = seg[i];
      vector<pair<R, int>> ps;
      REP(j, n) if (s.online(p[j])) ps.emplace_back(norm(p[j] - s[0]), j);
      sort(ALL(ps));
      REP(j, (int)ps.size() - 1) {
        const int u = ps[j].second;
        const int v = ps[j + 1].second;
        g[u].emplace_back(u, v, 0, abs(p[u] - p[v]));
        g[v].emplace_back(v, u, 0, abs(p[u] - p[v]));
      }
    }
  }
  int getIdx(P q) {
    auto it = lower_bound(ALL(p), q);
    if (it == p.end() || *it != q)
      return -1;
    return it - p.begin();
  }
};
struct DualGraph {
  struct DEdge {
    int u, v, f, l;
    R a;
    DEdge(int u, int v, R a) : u(u), v(v), f(0), l(0) {
      while (PI < a)
        a -= 2 * PI;
      while (a < -PI)
        a += 2 * PI;
      this->a = a;
    }
    bool operator==(const DEdge &opp) const { return v == opp.v; }
    bool operator<(const DEdge &opp) const { return a > opp.a; }
    bool operator<(const R &opp) const { return a > opp; }
    friend ostream &operator<<(ostream &os, const DEdge &t) {
      return os << "(" << t.u << "," << t.v << "," << t.a * 180 / PI << ")";
    }
  };
  int n;
  vector<P> p;
  vector<vector<DEdge>> g;
  UnionFind uf;
  DualGraph(const vector<P> &p)
      : p(p), g(p.size()), n(p.size()), uf(p.size()) {}
  void add_edge(const int s, const int t) {
    R a = arg(p[t] - p[s]);
    g[s].emplace_back(s, t, a);
    g[t].emplace_back(t, s, a > 0 ? a - PI : a + PI);
    uf.unionSet(s, t);
  }
  vector<G> poly;
  void add_polygon(int s, int t, R a) {
    auto e = lower_bound(ALL(g[s]), a - EPS);
    if (e == g[s].end())
      e = g[s].begin();
    if (e->f)
      return;
    e->f = 1;
    e->l = t;
    poly[t].push_back(p[s]);
    add_polygon(e->v, t, e->a > 0 ? e->a - PI : e->a + PI);
  }
  void toSpanningTree() {
    vector<pair<R, pii>> e;
    REP(i, n)
    REP(j, i) if (!uf.findSet(i, j))
        e.emplace_back(norm(p[i] - p[j]), pii(i, j));
    sort(ALL(e));
    REP(ii, e.size()) {
      const int i = e[ii].second.first;
      const int j = e[ii].second.second;
      if (uf.findSet(i, j))
        continue;
      const S s(p[i], p[j]);
      if ([&]() {
            REP(k, n) if (i != k && j != k) {
              FOR(it, g[k])
              if (intersect(s, S(p[k], p[it->v])) == TRUE)
                return 0;
            }
            return 1;
          }())
        add_edge(i, j);
    }
  }
  vector<vi> dual() {
    toSpanningTree();
    REP(i, n) {
      sort(ALL(g[i]));
      UNIQUE(g[i]);
    }
    int s = min_element(ALL(p)) - p.begin();
    poly.emplace_back();
    add_polygon(s, poly.size() - 1, -PI * .5);
    REP(i, n) REP(j, g[i].size()) if (!g[i][j].f) {
      poly.emplace_back();
      add_polygon(i, poly.size() - 1, g[i][j].a + 2. * EPS);
    }
    int m = poly.size();
    vector<vi> dg(m, vi(m, 1 << 20));
    REP(i, m) dg[i][i] = 0;
    vector<unordered_map<int, int>> rev(n);
    REP(i, n) REP(j, g[i].size()) {
      int u = i, v = g[i][j].v, l = g[i][j].l;
      if (u > v)
        swap(u, v);
      if (rev[u].find(v) != rev[u].end()) {
        if (l != rev[u][v]) {
          dg[l][rev[u][v]] = 1;
          dg[rev[u][v]][l] = 1;
        }
      } else
        rev[u][v] = l;
    }
    return dg;
  }
};
#undef SELF
#undef at
} // namespace geom
using namespace geom;
namespace std {
bool operator<(const P &a, const P &b) {
  return sig(a.X - b.X) ? a.X < b.X : a.Y < b.Y;
}
bool operator==(const P &a, const P &b) { return !sig(norm(a - b)); }
istream &operator>>(istream &is, P &p) {
  R x, y;
  is >> x >> y;
  p = P(x, y);
  return is;
}
istream &operator>>(istream &is, L &l) {
  l.resize(2);
  return is >> l[0] >> l[1];
}
istream &operator>>(istream &is, C &c) { return is >> (P &)c >> c.r; }
const double B = 500;
const double Z = 1;
ostream &operator<<(ostream &os, const C &c) {
  return os << "circle(" << B + Z * (c.X) << ", " << 1000 - B - Z * (c.Y)
            << ", " << Z * (c.r) << ")";
}
ostream &operator<<(ostream &os, const P &p) { return os << C(p, 2.); }
ostream &operator<<(ostream &os, const S &s) {
  return os << "line(" << B + Z * (s[0].X) << ", " << 1000 - B - Z * (s[0].Y)
            << ", " << B + Z * (s[1].X) << ", " << 1000 - B - Z * (s[1].Y)
            << ")";
}
ostream &operator<<(ostream &os, const G &g) {
  REP(i, g.size()) cout << g.edge(i) << endl;
  return os;
}
} // namespace std
int n;
int main() {
  ios::sync_with_stdio(false);
  while (cin >> n) {
    vector<G> poly(n);
    vector<S> s;
    REP(i, n) {
      int x;
      cin >> x;
      poly[i].resize(x);
      REP(j, x) cin >> poly[i][j];
      REP(j, x) s.push_back(poly[i].edge(j));
    }
    Arrangement arg(s);
    DualGraph dg(arg.p);
    REP(i, arg.g.size()) FOR(e, arg.g[i]) dg.add_edge(e->u, e->v);
    vector<vi> g = dg.dual();
    const auto &area = dg.poly;
    int m = area.size();
    vi level(m, -1);
    level[0] = 0;
    REPS(i, m - 1) {
      level[i] = count_if(ALL(poly), [&](const G &arg) {
        REP(j, area[i].size()) {
          const S &s = area[i].edge(j);
          const P p = (s[0] + s[1]) * .5;
          if (!arg.contains(p))
            return 0;
        }
        return 1;
      });
      if (level[i] == -1)
        assert(false);
    }
    REP(i, m)
    REP(j, i) if (g[i][j] == 1) g[i][j] = g[j][i] = abs(level[i] - level[j]);
    REP(k, m) REP(i, m) REP(j, m) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
    int q;
    cin >> q;
    REP(i, q) {
      P s, t;
      cin >> s >> t;
      int su = 0, tu = 0;
      REPS(j, m - 1) {
        if (area[j].contains(s))
          su = j;
        if (area[j].contains(t))
          tu = j;
      }
      cout << g[su][tu] << endl;
    }
  }
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 4044
final_rank: 3

code_snippet: #include <algorithm>
#include <assert.h>
#include <bitset>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <valarray>
#include <vector>
using namespace std;
typedef long long int ll;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define REP(i, x) for (int i = 0; i < (int)(x); i++)
#define REPS(i, x) for (int i = 1; i <= (int)(x); i++)
#define RREP(i, x) for (int i = ((int)(x)-1); i >= 0; i--)
#define RREPS(i, x) for (int i = ((int)(x)); i > 0; i--)
#define FOR(i, c)                                                              \
  for (__typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define RFOR(i, c)                                                             \
  for (__typeof((c).rbegin()) i = (c).rbegin(); i != (c).rend(); i++)
#define ALL(container) (container).begin(), (container).end()
#define RALL(container) (container).rbegin(), (container).rend()
#define SZ(container) ((int)container.size())
#define mp(a, b) make_pair(a, b)
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
template <class T> bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &t) {
  os << "[";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "]";
  return os;
}
template <class T> ostream &operator<<(ostream &os, const set<T> &t) {
  os << "{";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "}";
  return os;
}
template <class S, class T>
ostream &operator<<(ostream &os, const pair<S, T> &t) {
  return os << "(" << t.first << "," << t.second << ")";
}
template <class S, class T>
pair<S, T> operator+(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first + t.first, s.second + t.second);
}
template <class S, class T>
pair<S, T> operator-(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first - t.first, s.second - t.second);
}
struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x])
        swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};
namespace geom {
#define X real()
#define Y imag()
#define at(i) ((*this)[i])
#define SELF (*this)
enum { TRUE = 1, FALSE = 0, BORDER = -1 };
typedef int BOOL;
typedef double R;
const R INF = 1e8;
const R EPS = 1e-8;
const R PI = 3.1415926535897932384626;
inline int sig(const R &x) { return (abs(x) < EPS ? 0 : x > 0 ? 1 : -1); }
inline BOOL less(const R &x, const R &y) { return sig(x - y) ? x < y : BORDER; }
typedef complex<R> P;
inline R norm(const P &p) { return p.X * p.X + p.Y * p.Y; }
inline R inp(const P &a, const P &b) { return (conj(a) * b).X; }
inline R outp(const P &a, const P &b) { return (conj(a) * b).Y; }
inline P unit(const P &p) { return p / abs(p); }
inline P proj(const P &s, const P &t) { return t * inp(s, t) / norm(t); }
inline int ccw(const P &s, const P &t, const P &p, int adv = 0) {
  int res = sig(outp(t - s, p - s));
  if (res || !adv)
    return res;
  if (sig(inp(t - s, p - s)) < 0)
    return -2;
  if (sig(inp(s - t, p - t)) < 0)
    return 2;
  return 0;
}
struct L : public vector<P> {
  L(const P &p1, const P &p2) {
    this->push_back(p1);
    this->push_back(p2);
  }
  L() {}
  P dir() const { return at(1) - at(0); }
  BOOL online(const P &p) const { return !sig(outp(p - at(0), dir())); }
};
struct S : public L {
  S(const P &p1, const P &p2) : L(p1, p2) {}
  S() {}
  BOOL online(const P &p) const {
    if (!sig(norm(p - at(0))) || !sig(norm(p - at(1))))
      return BORDER;
    return !sig(abs(at(0) - p) + abs(at(1) - p) - abs(at(0) - at(1)));
  }
};
struct C : public P {
  C() {}
  C(const P &p, const R r) : P(p), r(r) {}
  R r;
  BOOL inside(const P &p) const { return less(norm(p - SELF), r * r); }
};
struct F : public C {
  R s, t;
  F(const C &c, R ss, R tt) : C(c), s(ss), t(tt) {
    if (PI < s)
      s -= 2 * PI;
    if (PI < t)
      t -= 2 * PI;
  }
  BOOL inside(const P &p) const {
    P v = p - SELF;
    if (!sig(norm(v)))
      return BORDER;
    R a = arg(v);
    if (t < s) {
      if ((!less(s, a) && !less(a, t)) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    } else {
      if (!less(s, a) || !less(a, t) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    }
  }
};
P crosspoint(const L &l, const L &m);
struct G : public vector<P> {
  G(size_type size = 0) : vector(size) {}
  S edge(int i) const { return S(at(i), at(i + 1 == size() ? 0 : i + 1)); }
  BOOL contains(const P &p) const {
    R sum = .0;
    REP(i, size()) {
      if (S(at(i), at((i + 1) % size())).online(p))
        return BORDER;
      sum += arg((at(i) - p) / (at((i + 1) % size()) - p));
    }
    return !!sig(sum);
  }
  R area() const {
    R sum = 0;
    REP(i, size()) sum += outp(at(i), at((i + 1) % size()));
    return abs(sum / 2.);
  }
  G convex_hull(bool online = false) {
    if (size() < 2)
      return *this;
    sort(ALL(*this));
    G r;
    r.resize((int)size() * 2);
    int k = 0;
    for (int i = 0; i < size(); r[k++] = at(i++))
      while (k > 1 && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    int t = k;
    for (int i = (int)size() - 1; i >= 0; r[k++] = at(i--))
      while (k > t && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    r.resize(k - 1);
    return r;
  }
  G cut(const L &l) const {
    G g;
    REP(i, size()) {
      const S &s = edge(i);
      if (ccw(l[0], l[1], s[0], 0) >= 0)
        g.push_back(s[0]);
      if (ccw(l[0], l[1], s[0], 0) * ccw(l[0], l[1], s[1], 0) < 0)
        g.push_back(crosspoint(s, l));
    }
    return g;
  }
  G Voronoi(const vector<P> &p, const int t) const {
    G g = *this;
    REP(i, p.size()) if (i != t) {
      const P m = (p[t] + p[i]) * 0.5;
      g = g.cut(L(m, m + (p[i] - p[t]) * P(0, 1)));
    }
    return g;
  }
};
inline P proj(const P &s, const L &t) {
  return t[0] + proj(s - t[0], t[1] - t[0]);
}
inline P reflect(const P &s, const L &t) { return 2. * proj(s, t) - s; }
inline S reflect(const S &s, const L &t) {
  return S(reflect(s[0], t), reflect(s[1], t));
}
BOOL intersect(const S &s, const S &t) {
  const int p = ccw(t[0], t[1], s[0], 1) * ccw(t[0], t[1], s[1], 1);
  const int q = ccw(s[0], s[1], t[0], 1) * ccw(s[0], s[1], t[1], 1);
  return (p > 0 || q > 0) ? FALSE : (!p || !q) ? BORDER : TRUE;
}
BOOL intersect(const S &s, const L &l) {
  if (l.online(s[0]) || l.online(s[1]))
    return BORDER;
  return (sig(outp(l.dir(), s[0] - l[0])) * sig(outp(l.dir(), s[1] - l[0])) <=
          0);
}
R dist2(const L &l, const P &p) {
  return norm(outp(l.dir(), p - l[0])) / norm(l.dir());
}
R dist2(const S &s, const P &p) {
  if (inp(p - s[0], s.dir()) < EPS)
    return norm(p - s[0]);
  if (inp(p - s[1], -s.dir()) < EPS)
    return norm(p - s[1]);
  return dist2((const L &)s, p);
}
R dist2(const S &s, const L &l) {
  return intersect(s, l) ? .0 : min(dist2(l, s[0]), dist2(l, s[1]));
}
R dist2(const S &s, const S &t) {
  return intersect(s, t) ? .0
                         : min(min(dist2(s, t[0]), dist2(t, s[0])),
                               min(dist2(s, t[1]), dist2(t, s[1])));
}
template <class T> R dist2(const G &g, const T &t) {
  R res = INF;
  REP(i, g.size()) res = min(res, dist2(g.edge(i), t));
  return res;
}
template <class S, class T> R dist(const S &s, const T &t) {
  return sqrt(dist2(s, t));
}
inline BOOL intersect(const C &a, const C &b) {
  return less((a.r - b.r) * (a.r - b.r), norm(a - b)) +
         less(norm(a - b), (a.r + b.r) * (a.r + b.r)) - 1;
}
inline BOOL intersect(const C &c, const L &l) {
  return less(dist2(l, c), c.r * c.r);
}
inline BOOL intersect(const C &c, const S &s) {
  int d = less(dist2(s, c), c.r * c.r);
  if (d != TRUE)
    return d;
  int p = c.inside(s[0]), q = c.inside(s[1]);
  return (p < 0 || q < 0) ? BORDER : p & q;
}
inline S crosspoint(const C &c1, const C &c2) {
  if (!intersect(c1, c2))
    return S();
  R d = abs(c1 - c2);
  R x = (c1.r * c1.r - c2.r * c2.r + d * d) / (2 * d);
  R h = sqrt(c1.r * c1.r - x * x);
  P u = unit(c2 - c1);
  return S(c1 + u * x + u * P(0, -1) * h, c1 + u * x + u * P(0, 1) * h);
}
inline P crosspoint(const L &l, const L &m) {
  R A = outp(l.dir(), m.dir()), B = outp(l.dir(), l[1] - m[0]);
  if (!sig(abs(A)) && !sig(abs(B)))
    return m[0];
  if (abs(A) < EPS)
    assert(false);
  return m[0] + B / A * (m[1] - m[0]);
}
inline S crosspoint(const C &c, const L &l) {
  R d2 = dist2(l, c);
  if (c.r * c.r + EPS < d2)
    return S();
  P m = proj(c, l);
  P u = unit(l[1] - l[0]);
  R d = sqrt(c.r * c.r - d2);
  return S(m + u * d, m - u * d);
}
inline vector<P> crosspoint(const C &c, const S &s) {
  vector<P> res = crosspoint(c, (const L &)s);
  RREP(i, res.size()) {
    if (inp(res[i] - s[0], s.dir()) * inp(res[i] - s[1], -s.dir()) < EPS)
      res.erase(res.begin() + i);
  }
  return res;
}
inline R commonarea(const C &a, const C &b) {
  if (less(norm(a - b), (a.r - b.r) * (a.r - b.r)) == TRUE)
    return min(a.r * a.r, b.r * b.r) * PI;
  if (less((a.r + b.r) * (a.r + b.r), norm(a - b)) == TRUE)
    return .0;
  double d = abs(a - b);
  double rc = (d * d + a.r * a.r - b.r * b.r) / (2 * d);
  double theta = acos(rc / a.r);
  double phi = acos((d - rc) / b.r);
  return a.r * a.r * theta + b.r * b.r * phi - d * a.r * sin(theta);
}
vector<L> CommonTangent(C c1, C c2) {
  if (c1.r > c2.r)
    swap(c1, c2);
  double d = abs(c1 - c2);
  vector<L> res;
  if (d < EPS)
    return res;
  if (d + EPS > c1.r + c2.r) {
    P crs = (c1 * c2.r + c2 * c1.r) / (c1.r + c2.r);
    double rad = asin(c1.r / abs(crs - c1));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., rad)));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., -rad)));
  }
  if (c1.r + d + EPS > c2.r) {
    double rad = 0.5 * PI + asin((c2.r - c1.r) / d);
    P v = unit(c2 - c1) * polar(1., rad);
    if (c1.r + d - EPS < c2.r) {
      res.push_back(L(c1 + v * c1.r, c1 + v * c1.r + (c1 - c2) * P(0, 1)));
    } else {
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
      v = 2. * proj(v, c2 - c1) - v;
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
    }
  }
  return res;
}
struct min_ball {
  P center;
  R radius2;
  min_ball(const vector<P> &p) { FOR(it, p) ps.push_back(*it); }
  min_ball &compile() {
    m = 0;
    center = P(0, 0);
    radius2 = -1;
    make_ball(ps.end());
    return *this;
  }

private:
  list<P> ps;
  list<P>::iterator supp_end;
  int m;
  P v[3], c[3];
  R z[3], r[3];
  void pop() { --m; }
  void push(const P &p) {
    if (m == 0) {
      c[0] = p;
      r[0] = 0;
    } else {
      R e = norm(p - c[m - 1]) - r[m - 1];
      P delta = p - c[0];
      v[m] = p - c[0];
      for (int i = 1; i < m; ++i)
        v[m] -= v[i] * inp(v[i], delta) / z[i];
      z[m] = inp(v[m], v[m]);
      c[m] = c[m - 1] + e * v[m] / z[m] * .5;
      r[m] = r[m - 1] + e * e / z[m] * .25;
    }
    center = c[m];
    radius2 = r[m];
    ++m;
  }
  void make_ball(list<P>::iterator i) {
    supp_end = ps.begin();
    if (m == 3)
      return;
    for (list<P>::iterator k = ps.begin(); k != i;) {
      list<P>::iterator j = k++;
      if (norm(*j - center) > radius2) {
        push(*j);
        make_ball(j);
        pop();
        move_to_front(j);
      }
    }
  }
  void move_to_front(list<P>::iterator j) {
    if (supp_end == j)
      ++supp_end;
    ps.splice(ps.begin(), ps, j);
  }
};
struct Arrangement {
  struct AEdge {
    int u, v, t;
    R cost;
    AEdge(int u = 0, int v = 0, int t = 0, R cost = 0)
        : u(u), v(v), t(t), cost(cost) {}
  };
  typedef vector<vector<AEdge>> AGraph;
  vector<P> p;
  AGraph g;
  Arrangement() {}
  Arrangement(vector<S> seg) {
    int m = seg.size();
    REP(i, m) {
      p.push_back(seg[i][0]);
      p.push_back(seg[i][1]);
      REP(j, i)
      if (sig(outp(seg[i].dir(), seg[j].dir())) &&
          intersect(seg[i], seg[j]) == TRUE)
          p.push_back(crosspoint(seg[i], seg[j]));
    }
    sort(ALL(p));
    UNIQUE(p);
    int n = p.size();
    g.resize(n);
    REP(i, m) {
      S &s = seg[i];
      vector<pair<R, int>> ps;
      REP(j, n) if (s.online(p[j])) ps.emplace_back(norm(p[j] - s[0]), j);
      sort(ALL(ps));
      REP(j, (int)ps.size() - 1) {
        const int u = ps[j].second;
        const int v = ps[j + 1].second;
        g[u].emplace_back(u, v, 0, abs(p[u] - p[v]));
        g[v].emplace_back(v, u, 0, abs(p[u] - p[v]));
      }
    }
  }
  int getIdx(P q) {
    auto it = lower_bound(ALL(p), q);
    if (it == p.end() || *it != q)
      return -1;
    return it - p.begin();
  }
};
struct DualGraph {
  struct DEdge {
    int u, v, f, l;
    R a;
    DEdge(int u, int v, R a) : u(u), v(v), f(0), l(0) {
      while (PI < a)
        a -= 2 * PI;
      while (a < -PI)
        a += 2 * PI;
      this->a = a;
    }
    bool operator==(const DEdge &opp) const { return v == opp.v; }
    bool operator<(const DEdge &opp) const { return a > opp.a; }
    bool operator<(const R &opp) const { return a > opp; }
    friend ostream &operator<<(ostream &os, const DEdge &t) {
      return os << "(" << t.u << "," << t.v << "," << t.a * 180 / PI << ")";
    }
  };
  int n;
  vector<P> p;
  vector<vector<DEdge>> g;
  UnionFind uf;
  DualGraph(const vector<P> &p)
      : p(p), g(p.size()), n(p.size()), uf(p.size()) {}
  void add_edge(const int s, const int t) {
    R a = arg(p[t] - p[s]);
    g[s].emplace_back(s, t, a);
    g[t].emplace_back(t, s, a > 0 ? a - PI : a + PI);
    uf.unionSet(s, t);
  }
  vector<G> poly;
  void add_polygon(int s, int t, R a) {
    auto e = lower_bound(ALL(g[s]), a - EPS);
    if (e == g[s].end())
      e = g[s].begin();
    if (e->f)
      return;
    e->f = 1;
    e->l = t;
    poly[t].push_back(p[s]);
    add_polygon(e->v, t, e->a > 0 ? e->a - PI : e->a + PI);
  }
  void toSpanningTree() {
    vector<pair<R, pii>> e;
    REP(i, n)
    REP(j, i) if (!uf.findSet(i, j))
        e.emplace_back(norm(p[i] - p[j]), pii(i, j));
    sort(ALL(e));
    REP(ii, e.size()) {
      const int i = e[ii].second.first;
      const int j = e[ii].second.second;
      if (uf.findSet(i, j))
        continue;
      const S s(p[i], p[j]);
      if ([&]() {
            REP(k, n) if (i != k && j != k) {
              FOR(it, g[k])
              if (intersect(s, S(p[k], p[it->v])) == TRUE)
                return 0;
            }
            return 1;
          }())
        add_edge(i, j);
    }
  }
  vector<vi> dual() {
    toSpanningTree();
    REP(i, n) {
      sort(ALL(g[i]));
      UNIQUE(g[i]);
    }
    int s = min_element(ALL(p)) - p.begin();
    poly.emplace_back();
    add_polygon(s, poly.size() - 1, -PI * .5);
    REP(i, n) REP(j, g[i].size()) if (!g[i][j].f) {
      poly.emplace_back();
      add_polygon(i, poly.size() - 1, g[i][j].a + 2. * EPS);
    }
    int m = poly.size();
    vector<vi> dg(m, vi(m, 1 << 20));
    REP(i, m) dg[i][i] = 0;
    vector<unordered_map<int, int>> rev(n);
    REP(i, n) REP(j, g[i].size()) {
      int u = i, v = g[i][j].v, l = g[i][j].l;
      if (u > v)
        swap(u, v);
      if (rev[u].find(v) != rev[u].end()) {
        if (l != rev[u][v]) {
          dg[l][rev[u][v]] = 1;
          dg[rev[u][v]][l] = 1;
        }
      } else
        rev[u][v] = l;
    }
    return dg;
  }
};
#undef SELF
#undef at
} // namespace geom
using namespace geom;
namespace std {
bool operator<(const P &a, const P &b) {
  return sig(a.X - b.X) ? a.X < b.X : a.Y < b.Y;
}
bool operator==(const P &a, const P &b) { return !sig(norm(a - b)); }
istream &operator>>(istream &is, P &p) {
  R x, y;
  is >> x >> y;
  p = P(x, y);
  return is;
}
istream &operator>>(istream &is, L &l) {
  l.resize(2);
  return is >> l[0] >> l[1];
}
istream &operator>>(istream &is, C &c) { return is >> (P &)c >> c.r; }
const double B = 500;
const double Z = 1;
ostream &operator<<(ostream &os, const C &c) {
  return os << "circle(" << B + Z * (c.X) << ", " << 1000 - B - Z * (c.Y)
            << ", " << Z * (c.r) << ")";
}
ostream &operator<<(ostream &os, const P &p) { return os << C(p, 2.); }
ostream &operator<<(ostream &os, const S &s) {
  return os << "line(" << B + Z * (s[0].X) << ", " << 1000 - B - Z * (s[0].Y)
            << ", " << B + Z * (s[1].X) << ", " << 1000 - B - Z * (s[1].Y)
            << ")";
}
ostream &operator<<(ostream &os, const G &g) {
  REP(i, g.size()) cout << g.edge(i) << endl;
  return os;
}
} // namespace std
int n;
int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  ios::sync_with_stdio(false);
  while (cin >> n) {
    vector<G> poly(n);
    vector<S> s;
    REP(i, n) {
      int x;
      cin >> x;
      poly[i].resize(x);
      REP(j, x) cin >> poly[i][j];
      REP(j, x) s.push_back(poly[i].edge(j));
    }
    Arrangement arg(s);
    DualGraph dg(arg.p);
    REP(i, arg.g.size()) FOR(e, arg.g[i]) dg.add_edge(e->u, e->v);
    vector<vi> g = dg.dual();
    const auto &area = dg.poly;
    int m = area.size();
    vi level(m, -1);
    level[0] = 0;
    REPS(i, m - 1) {
      level[i] = count_if(ALL(poly), [&](const G &arg) {
        REP(j, area[i].size()) {
          const S &s = area[i].edge(j);
          const P p = (s[0] + s[1]) * .5;
          if (!arg.contains(p))
            return 0;
        }
        return 1;
      });
      if (level[i] == -1)
        assert(false);
    }
    REP(i, m)
    REP(j, i) if (g[i][j] == 1) g[i][j] = g[j][i] = abs(level[i] - level[j]);
    REP(k, m) REP(i, m) REP(j, m) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
    int q;
    cin >> q;
    REP(i, q) {
      P s, t;
      cin >> s >> t;
      int su = 0, tu = 0;
      REPS(j, m - 1) {
        if (area[j].contains(s))
          su = j;
        if (area[j].contains(t))
          tu = j;
      }
      cout << g[su][tu] << endl;
    }
  }
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3984
final_rank: 2

code_snippet: #pragma GCC optimize("O3")
#pragma GCC target("sse4")
#include <algorithm>
#include <assert.h>
#include <bitset>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <valarray>
#include <vector>
using namespace std;
typedef long long int ll;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define REP(i, x) for (int i = 0; i < (int)(x); i++)
#define REPS(i, x) for (int i = 1; i <= (int)(x); i++)
#define RREP(i, x) for (int i = ((int)(x)-1); i >= 0; i--)
#define RREPS(i, x) for (int i = ((int)(x)); i > 0; i--)
#define FOR(i, c)                                                              \
  for (__typeof((c).begin()) i = (c).begin(); i != (c).end(); i++)
#define RFOR(i, c)                                                             \
  for (__typeof((c).rbegin()) i = (c).rbegin(); i != (c).rend(); i++)
#define ALL(container) (container).begin(), (container).end()
#define RALL(container) (container).rbegin(), (container).rend()
#define SZ(container) ((int)container.size())
#define mp(a, b) make_pair(a, b)
#define UNIQUE(v) v.erase(unique(v.begin(), v.end()), v.end());
template <class T> bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
template <class T> ostream &operator<<(ostream &os, const vector<T> &t) {
  os << "[";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "]";
  return os;
}
template <class T> ostream &operator<<(ostream &os, const set<T> &t) {
  os << "{";
  FOR(it, t) {
    if (it != t.begin())
      os << ",";
    os << *it;
  }
  os << "}";
  return os;
}
template <class S, class T>
ostream &operator<<(ostream &os, const pair<S, T> &t) {
  return os << "(" << t.first << "," << t.second << ")";
}
template <class S, class T>
pair<S, T> operator+(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first + t.first, s.second + t.second);
}
template <class S, class T>
pair<S, T> operator-(const pair<S, T> &s, const pair<S, T> &t) {
  return pair<S, T>(s.first - t.first, s.second - t.second);
}
struct UnionFind {
  vector<int> data;
  UnionFind(int size) : data(size, -1) {}
  bool unionSet(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) {
      if (data[y] < data[x])
        swap(x, y);
      data[x] += data[y];
      data[y] = x;
    }
    return x != y;
  }
  bool findSet(int x, int y) { return root(x) == root(y); }
  int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
  int size(int x) { return -data[root(x)]; }
};
namespace geom {
#define X real()
#define Y imag()
#define at(i) ((*this)[i])
#define SELF (*this)
enum { TRUE = 1, FALSE = 0, BORDER = -1 };
typedef int BOOL;
typedef double R;
const R INF = 1e8;
const R EPS = 1e-8;
const R PI = 3.1415926535897932384626;
inline int sig(const R &x) { return (abs(x) < EPS ? 0 : x > 0 ? 1 : -1); }
inline BOOL less(const R &x, const R &y) { return sig(x - y) ? x < y : BORDER; }
typedef complex<R> P;
inline R norm(const P &p) { return p.X * p.X + p.Y * p.Y; }
inline R inp(const P &a, const P &b) { return (conj(a) * b).X; }
inline R outp(const P &a, const P &b) { return (conj(a) * b).Y; }
inline P unit(const P &p) { return p / abs(p); }
inline P proj(const P &s, const P &t) { return t * inp(s, t) / norm(t); }
inline int ccw(const P &s, const P &t, const P &p, int adv = 0) {
  int res = sig(outp(t - s, p - s));
  if (res || !adv)
    return res;
  if (sig(inp(t - s, p - s)) < 0)
    return -2;
  if (sig(inp(s - t, p - t)) < 0)
    return 2;
  return 0;
}
struct L : public vector<P> {
  L(const P &p1, const P &p2) {
    this->push_back(p1);
    this->push_back(p2);
  }
  L() {}
  P dir() const { return at(1) - at(0); }
  BOOL online(const P &p) const { return !sig(outp(p - at(0), dir())); }
};
struct S : public L {
  S(const P &p1, const P &p2) : L(p1, p2) {}
  S() {}
  BOOL online(const P &p) const {
    if (!sig(norm(p - at(0))) || !sig(norm(p - at(1))))
      return BORDER;
    return !sig(abs(at(0) - p) + abs(at(1) - p) - abs(at(0) - at(1)));
  }
};
struct C : public P {
  C() {}
  C(const P &p, const R r) : P(p), r(r) {}
  R r;
  BOOL inside(const P &p) const { return less(norm(p - SELF), r * r); }
};
struct F : public C {
  R s, t;
  F(const C &c, R ss, R tt) : C(c), s(ss), t(tt) {
    if (PI < s)
      s -= 2 * PI;
    if (PI < t)
      t -= 2 * PI;
  }
  BOOL inside(const P &p) const {
    P v = p - SELF;
    if (!sig(norm(v)))
      return BORDER;
    R a = arg(v);
    if (t < s) {
      if ((!less(s, a) && !less(a, t)) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    } else {
      if (!less(s, a) || !less(a, t) || !less(norm(v), r * r))
        return FALSE;
      return less(s, a) | less(a, t) | less(norm(v), r * r);
    }
  }
};
P crosspoint(const L &l, const L &m);
struct G : public vector<P> {
  G(size_type size = 0) : vector(size) {}
  S edge(int i) const { return S(at(i), at(i + 1 == size() ? 0 : i + 1)); }
  BOOL contains(const P &p) const {
    R sum = .0;
    REP(i, size()) {
      if (S(at(i), at((i + 1) % size())).online(p))
        return BORDER;
      sum += arg((at(i) - p) / (at((i + 1) % size()) - p));
    }
    return !!sig(sum);
  }
  R area() const {
    R sum = 0;
    REP(i, size()) sum += outp(at(i), at((i + 1) % size()));
    return abs(sum / 2.);
  }
  G convex_hull(bool online = false) {
    if (size() < 2)
      return *this;
    sort(ALL(*this));
    G r;
    r.resize((int)size() * 2);
    int k = 0;
    for (int i = 0; i < size(); r[k++] = at(i++))
      while (k > 1 && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    int t = k;
    for (int i = (int)size() - 1; i >= 0; r[k++] = at(i--))
      while (k > t && ccw(r[k - 2], r[k - 1], at(i)) < 1 - online)
        k--;
    r.resize(k - 1);
    return r;
  }
  G cut(const L &l) const {
    G g;
    REP(i, size()) {
      const S &s = edge(i);
      if (ccw(l[0], l[1], s[0], 0) >= 0)
        g.push_back(s[0]);
      if (ccw(l[0], l[1], s[0], 0) * ccw(l[0], l[1], s[1], 0) < 0)
        g.push_back(crosspoint(s, l));
    }
    return g;
  }
  G Voronoi(const vector<P> &p, const int t) const {
    G g = *this;
    REP(i, p.size()) if (i != t) {
      const P m = (p[t] + p[i]) * 0.5;
      g = g.cut(L(m, m + (p[i] - p[t]) * P(0, 1)));
    }
    return g;
  }
};
inline P proj(const P &s, const L &t) {
  return t[0] + proj(s - t[0], t[1] - t[0]);
}
inline P reflect(const P &s, const L &t) { return 2. * proj(s, t) - s; }
inline S reflect(const S &s, const L &t) {
  return S(reflect(s[0], t), reflect(s[1], t));
}
BOOL intersect(const S &s, const S &t) {
  const int p = ccw(t[0], t[1], s[0], 1) * ccw(t[0], t[1], s[1], 1);
  const int q = ccw(s[0], s[1], t[0], 1) * ccw(s[0], s[1], t[1], 1);
  return (p > 0 || q > 0) ? FALSE : (!p || !q) ? BORDER : TRUE;
}
BOOL intersect(const S &s, const L &l) {
  if (l.online(s[0]) || l.online(s[1]))
    return BORDER;
  return (sig(outp(l.dir(), s[0] - l[0])) * sig(outp(l.dir(), s[1] - l[0])) <=
          0);
}
R dist2(const L &l, const P &p) {
  return norm(outp(l.dir(), p - l[0])) / norm(l.dir());
}
R dist2(const S &s, const P &p) {
  if (inp(p - s[0], s.dir()) < EPS)
    return norm(p - s[0]);
  if (inp(p - s[1], -s.dir()) < EPS)
    return norm(p - s[1]);
  return dist2((const L &)s, p);
}
R dist2(const S &s, const L &l) {
  return intersect(s, l) ? .0 : min(dist2(l, s[0]), dist2(l, s[1]));
}
R dist2(const S &s, const S &t) {
  return intersect(s, t) ? .0
                         : min(min(dist2(s, t[0]), dist2(t, s[0])),
                               min(dist2(s, t[1]), dist2(t, s[1])));
}
template <class T> R dist2(const G &g, const T &t) {
  R res = INF;
  REP(i, g.size()) res = min(res, dist2(g.edge(i), t));
  return res;
}
template <class S, class T> R dist(const S &s, const T &t) {
  return sqrt(dist2(s, t));
}
inline BOOL intersect(const C &a, const C &b) {
  return less((a.r - b.r) * (a.r - b.r), norm(a - b)) +
         less(norm(a - b), (a.r + b.r) * (a.r + b.r)) - 1;
}
inline BOOL intersect(const C &c, const L &l) {
  return less(dist2(l, c), c.r * c.r);
}
inline BOOL intersect(const C &c, const S &s) {
  int d = less(dist2(s, c), c.r * c.r);
  if (d != TRUE)
    return d;
  int p = c.inside(s[0]), q = c.inside(s[1]);
  return (p < 0 || q < 0) ? BORDER : p & q;
}
inline S crosspoint(const C &c1, const C &c2) {
  if (!intersect(c1, c2))
    return S();
  R d = abs(c1 - c2);
  R x = (c1.r * c1.r - c2.r * c2.r + d * d) / (2 * d);
  R h = sqrt(c1.r * c1.r - x * x);
  P u = unit(c2 - c1);
  return S(c1 + u * x + u * P(0, -1) * h, c1 + u * x + u * P(0, 1) * h);
}
inline P crosspoint(const L &l, const L &m) {
  R A = outp(l.dir(), m.dir()), B = outp(l.dir(), l[1] - m[0]);
  if (!sig(abs(A)) && !sig(abs(B)))
    return m[0];
  if (abs(A) < EPS)
    assert(false);
  return m[0] + B / A * (m[1] - m[0]);
}
inline S crosspoint(const C &c, const L &l) {
  R d2 = dist2(l, c);
  if (c.r * c.r + EPS < d2)
    return S();
  P m = proj(c, l);
  P u = unit(l[1] - l[0]);
  R d = sqrt(c.r * c.r - d2);
  return S(m + u * d, m - u * d);
}
inline vector<P> crosspoint(const C &c, const S &s) {
  vector<P> res = crosspoint(c, (const L &)s);
  RREP(i, res.size()) {
    if (inp(res[i] - s[0], s.dir()) * inp(res[i] - s[1], -s.dir()) < EPS)
      res.erase(res.begin() + i);
  }
  return res;
}
inline R commonarea(const C &a, const C &b) {
  if (less(norm(a - b), (a.r - b.r) * (a.r - b.r)) == TRUE)
    return min(a.r * a.r, b.r * b.r) * PI;
  if (less((a.r + b.r) * (a.r + b.r), norm(a - b)) == TRUE)
    return .0;
  double d = abs(a - b);
  double rc = (d * d + a.r * a.r - b.r * b.r) / (2 * d);
  double theta = acos(rc / a.r);
  double phi = acos((d - rc) / b.r);
  return a.r * a.r * theta + b.r * b.r * phi - d * a.r * sin(theta);
}
vector<L> CommonTangent(C c1, C c2) {
  if (c1.r > c2.r)
    swap(c1, c2);
  double d = abs(c1 - c2);
  vector<L> res;
  if (d < EPS)
    return res;
  if (d + EPS > c1.r + c2.r) {
    P crs = (c1 * c2.r + c2 * c1.r) / (c1.r + c2.r);
    double rad = asin(c1.r / abs(crs - c1));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., rad)));
    res.push_back(L(crs, crs + (c1 - crs) * polar(1., -rad)));
  }
  if (c1.r + d + EPS > c2.r) {
    double rad = 0.5 * PI + asin((c2.r - c1.r) / d);
    P v = unit(c2 - c1) * polar(1., rad);
    if (c1.r + d - EPS < c2.r) {
      res.push_back(L(c1 + v * c1.r, c1 + v * c1.r + (c1 - c2) * P(0, 1)));
    } else {
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
      v = 2. * proj(v, c2 - c1) - v;
      res.push_back(L(c1 + v * c1.r, c2 + v * c2.r));
    }
  }
  return res;
}
struct min_ball {
  P center;
  R radius2;
  min_ball(const vector<P> &p) { FOR(it, p) ps.push_back(*it); }
  min_ball &compile() {
    m = 0;
    center = P(0, 0);
    radius2 = -1;
    make_ball(ps.end());
    return *this;
  }

private:
  list<P> ps;
  list<P>::iterator supp_end;
  int m;
  P v[3], c[3];
  R z[3], r[3];
  void pop() { --m; }
  void push(const P &p) {
    if (m == 0) {
      c[0] = p;
      r[0] = 0;
    } else {
      R e = norm(p - c[m - 1]) - r[m - 1];
      P delta = p - c[0];
      v[m] = p - c[0];
      for (int i = 1; i < m; ++i)
        v[m] -= v[i] * inp(v[i], delta) / z[i];
      z[m] = inp(v[m], v[m]);
      c[m] = c[m - 1] + e * v[m] / z[m] * .5;
      r[m] = r[m - 1] + e * e / z[m] * .25;
    }
    center = c[m];
    radius2 = r[m];
    ++m;
  }
  void make_ball(list<P>::iterator i) {
    supp_end = ps.begin();
    if (m == 3)
      return;
    for (list<P>::iterator k = ps.begin(); k != i;) {
      list<P>::iterator j = k++;
      if (norm(*j - center) > radius2) {
        push(*j);
        make_ball(j);
        pop();
        move_to_front(j);
      }
    }
  }
  void move_to_front(list<P>::iterator j) {
    if (supp_end == j)
      ++supp_end;
    ps.splice(ps.begin(), ps, j);
  }
};
struct Arrangement {
  struct AEdge {
    int u, v, t;
    R cost;
    AEdge(int u = 0, int v = 0, int t = 0, R cost = 0)
        : u(u), v(v), t(t), cost(cost) {}
  };
  typedef vector<vector<AEdge>> AGraph;
  vector<P> p;
  AGraph g;
  Arrangement() {}
  Arrangement(vector<S> seg) {
    int m = seg.size();
    REP(i, m) {
      p.push_back(seg[i][0]);
      p.push_back(seg[i][1]);
      REP(j, i)
      if (sig(outp(seg[i].dir(), seg[j].dir())) &&
          intersect(seg[i], seg[j]) == TRUE)
          p.push_back(crosspoint(seg[i], seg[j]));
    }
    sort(ALL(p));
    UNIQUE(p);
    int n = p.size();
    g.resize(n);
    REP(i, m) {
      S &s = seg[i];
      vector<pair<R, int>> ps;
      REP(j, n) if (s.online(p[j])) ps.emplace_back(norm(p[j] - s[0]), j);
      sort(ALL(ps));
      REP(j, (int)ps.size() - 1) {
        const int u = ps[j].second;
        const int v = ps[j + 1].second;
        g[u].emplace_back(u, v, 0, abs(p[u] - p[v]));
        g[v].emplace_back(v, u, 0, abs(p[u] - p[v]));
      }
    }
  }
  int getIdx(P q) {
    auto it = lower_bound(ALL(p), q);
    if (it == p.end() || *it != q)
      return -1;
    return it - p.begin();
  }
};
struct DualGraph {
  struct DEdge {
    int u, v, f, l;
    R a;
    DEdge(int u, int v, R a) : u(u), v(v), f(0), l(0) {
      while (PI < a)
        a -= 2 * PI;
      while (a < -PI)
        a += 2 * PI;
      this->a = a;
    }
    bool operator==(const DEdge &opp) const { return v == opp.v; }
    bool operator<(const DEdge &opp) const { return a > opp.a; }
    bool operator<(const R &opp) const { return a > opp; }
    friend ostream &operator<<(ostream &os, const DEdge &t) {
      return os << "(" << t.u << "," << t.v << "," << t.a * 180 / PI << ")";
    }
  };
  int n;
  vector<P> p;
  vector<vector<DEdge>> g;
  UnionFind uf;
  DualGraph(const vector<P> &p)
      : p(p), g(p.size()), n(p.size()), uf(p.size()) {}
  void add_edge(const int s, const int t) {
    R a = arg(p[t] - p[s]);
    g[s].emplace_back(s, t, a);
    g[t].emplace_back(t, s, a > 0 ? a - PI : a + PI);
    uf.unionSet(s, t);
  }
  vector<G> poly;
  void add_polygon(int s, int t, R a) {
    auto e = lower_bound(ALL(g[s]), a - EPS);
    if (e == g[s].end())
      e = g[s].begin();
    if (e->f)
      return;
    e->f = 1;
    e->l = t;
    poly[t].push_back(p[s]);
    add_polygon(e->v, t, e->a > 0 ? e->a - PI : e->a + PI);
  }
  void toSpanningTree() {
    vector<pair<R, pii>> e;
    REP(i, n)
    REP(j, i) if (!uf.findSet(i, j))
        e.emplace_back(norm(p[i] - p[j]), pii(i, j));
    sort(ALL(e));
    REP(ii, e.size()) {
      const int i = e[ii].second.first;
      const int j = e[ii].second.second;
      if (uf.findSet(i, j))
        continue;
      const S s(p[i], p[j]);
      if ([&]() {
            REP(k, n) if (i != k && j != k) {
              FOR(it, g[k])
              if (intersect(s, S(p[k], p[it->v])) == TRUE)
                return 0;
            }
            return 1;
          }())
        add_edge(i, j);
    }
  }
  vector<vi> dual() {
    toSpanningTree();
    REP(i, n) {
      sort(ALL(g[i]));
      UNIQUE(g[i]);
    }
    int s = min_element(ALL(p)) - p.begin();
    poly.emplace_back();
    add_polygon(s, poly.size() - 1, -PI * .5);
    REP(i, n) REP(j, g[i].size()) if (!g[i][j].f) {
      poly.emplace_back();
      add_polygon(i, poly.size() - 1, g[i][j].a + 2. * EPS);
    }
    int m = poly.size();
    vector<vi> dg(m, vi(m, 1 << 20));
    REP(i, m) dg[i][i] = 0;
    vector<unordered_map<int, int>> rev(n);
    REP(i, n) REP(j, g[i].size()) {
      int u = i, v = g[i][j].v, l = g[i][j].l;
      if (u > v)
        swap(u, v);
      if (rev[u].find(v) != rev[u].end()) {
        if (l != rev[u][v]) {
          dg[l][rev[u][v]] = 1;
          dg[rev[u][v]][l] = 1;
        }
      } else
        rev[u][v] = l;
    }
    return dg;
  }
};
#undef SELF
#undef at
} // namespace geom
using namespace geom;
namespace std {
bool operator<(const P &a, const P &b) {
  return sig(a.X - b.X) ? a.X < b.X : a.Y < b.Y;
}
bool operator==(const P &a, const P &b) { return !sig(norm(a - b)); }
istream &operator>>(istream &is, P &p) {
  R x, y;
  is >> x >> y;
  p = P(x, y);
  return is;
}
istream &operator>>(istream &is, L &l) {
  l.resize(2);
  return is >> l[0] >> l[1];
}
istream &operator>>(istream &is, C &c) { return is >> (P &)c >> c.r; }
const double B = 500;
const double Z = 1;
ostream &operator<<(ostream &os, const C &c) {
  return os << "circle(" << B + Z * (c.X) << ", " << 1000 - B - Z * (c.Y)
            << ", " << Z * (c.r) << ")";
}
ostream &operator<<(ostream &os, const P &p) { return os << C(p, 2.); }
ostream &operator<<(ostream &os, const S &s) {
  return os << "line(" << B + Z * (s[0].X) << ", " << 1000 - B - Z * (s[0].Y)
            << ", " << B + Z * (s[1].X) << ", " << 1000 - B - Z * (s[1].Y)
            << ")";
}
ostream &operator<<(ostream &os, const G &g) {
  REP(i, g.size()) cout << g.edge(i) << endl;
  return os;
}
} // namespace std
int n;
int main() {
  ios::sync_with_stdio(false);
  while (cin >> n) {
    vector<G> poly(n);
    vector<S> s;
    REP(i, n) {
      int x;
      cin >> x;
      poly[i].resize(x);
      REP(j, x) cin >> poly[i][j];
      REP(j, x) s.push_back(poly[i].edge(j));
    }
    Arrangement arg(s);
    DualGraph dg(arg.p);
    REP(i, arg.g.size()) FOR(e, arg.g[i]) dg.add_edge(e->u, e->v);
    vector<vi> g = dg.dual();
    const auto &area = dg.poly;
    int m = area.size();
    vi level(m, -1);
    level[0] = 0;
    REPS(i, m - 1) {
      level[i] = count_if(ALL(poly), [&](const G &arg) {
        REP(j, area[i].size()) {
          const S &s = area[i].edge(j);
          const P p = (s[0] + s[1]) * .5;
          if (!arg.contains(p))
            return 0;
        }
        return 1;
      });
      if (level[i] == -1)
        assert(false);
    }
    REP(i, m)
    REP(j, i) if (g[i][j] == 1) g[i][j] = g[j][i] = abs(level[i] - level[j]);
    REP(k, m) REP(i, m) REP(j, m) g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
    int q;
    cin >> q;
    REP(i, q) {
      P s, t;
      cin >> s >> t;
      int su = 0, tu = 0;
      REPS(j, m - 1) {
        if (area[j].contains(s))
          su = j;
        if (area[j].contains(t))
          tu = j;
      }
      cout << g[su][tu] << endl;
    }
  }
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3960
final_rank: 1

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (n); i++)
typedef double D;
typedef complex<D> P;
typedef pair<P, P> L;
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7;
#define X real()
#define Y imag()
#define LE(n, m) ((n) < (m) + EPS)
#define GE(n, m) ((n) + EPS > (m))
#define EQ(n, m) (abs((n) - (m)) < EPS)
D dot(P a, P b) { return (conj(a) * b).X; }
D cross(P a, P b) { return (conj(a) * b).Y; }
int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS)
    return +1;
  if (cross(b, c) < -EPS)
    return -1;
  if (dot(b, c) < -EPS)
    return +2;
  if (norm(b) < norm(c))
    return -2;
  return 0;
}
bool isparallel(L a, L b) {
  return cross(a.first - a.second, b.first - b.second) == 0.0;
}
bool isecLP(P a1, P a2, P b) { return abs(ccw(a1, a2, b)) != 1; }
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2 - a1, b2 - b1, 0) || isecLP(a1, b1, b2);
}
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2 - a1, b1 - a1) * cross(a2 - a1, b2 - a1) < EPS;
}
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;
}
bool isecSP(P a1, P a2, P b) {
  return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;
}
P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2 - b1, b1 - a1);
  D d2 = cross(b2 - b1, a2 - a1);
  if (EQ(d1, 0) && EQ(d2, 0))
    return a1;
  if (EQ(d2, 0))
    throw "kouten ga nai";
  return a1 + d1 / d2 * (a2 - a1);
}
namespace std {
bool operator<(const P a, const P b) {
  return a.X != b.X ? a.X < b.X : a.Y < b.Y;
}
} // namespace std
VP convexHull(VP ps) {
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2 * n);
  for (int i = 0; i < n; ch[k++] = ps[i++])
    while (k >= 2 && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0)
      --k;
  for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = ps[i--])
    while (k >= t && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0)
      --k;
  ch.resize(k - 1);
  return ch;
}
bool isCcwConvex(const VP &ps) {
  int n = ps.size();
  rep(i,
      n) if (ccw(ps[i], ps[(i + 1) % n], ps[(i + 2) % n]) == -1) return false;
  return true;
}
int inConvex(P p, const VP &ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep(i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc)
      return 2;
    if (ccwc != dir)
      return 0;
  }
  return 1;
}
int inPolygon(const VP &ps, P p) {
  int n = ps.size();
  bool in = false;
  rep(i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a, b), 0) && LE(dot(a, b), 0))
      return 2;
    if (a.Y > b.Y)
      swap(a, b);
    if ((a.Y * b.Y < 0 || (a.Y * b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0))
      in = !in;
  }
  return in;
}
VP convexCut(const VP &ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i, n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1)
      ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1)
      ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP &);
bool isec_CS(const VP &ps, P a1, P a2) {
  rep(i, ps.size()) {
    if (isecLS(ps[i], ps[(i + 1) % ps.size()], a1, a2))
      return true;
  }
  return false;
}
void divide_convex(VP ps, const VP &s1, const VP &s2, Vcon &V) {
  bool flag = false;
  for (int i = 0; i < s1.size(); i++) {
    VP convex_1 = convexCut(ps, s1[i], s2[i]);
    VP convex_2 = convexCut(ps, s2[i], s1[i]);
    if (area(convex_1) < EPS || area(convex_2) < EPS)
      continue;
    divide_convex(convex_1, s1, s2, V);
    divide_convex(convex_2, s1, s2, V);
    flag = true;
    break;
  }
  if (!flag)
    V.push_back(ps);
  return;
}
bool isecCC(VP a1, VP a2) {
  rep(i, a1.size()) {
    rep(j, a2.size()) {
      P e1 = a1[i];
      P e2 = a1[(i + 1) % a1.size()];
      P e3 = a2[j];
      P e4 = a2[(j + 1) % a2.size()];
      if (abs(e1 - e3) < EPS && abs(e2 - e4) < EPS)
        return 1;
      if (abs(e1 - e4) < EPS && abs(e2 - e3) < EPS)
        return 1;
    }
  }
  return 0;
}
vector<vector<int>> Convex_arrangement(const Vcon &ps) {
  int n = ps.size();
  vector<vector<int>> V(n, vector<int>(n));
  rep(i, n) rep(j, n) V[i][j] = 1e8;
  rep(i, n) {
    for (int j = i + 1; j < n; j++) {
      if (isecCC(ps[i], ps[j])) {
        V[i][j] = 1;
        V[j][i] = 1;
      }
    }
  }
  return V;
}
D area(const VP &ps) {
  D a = 0;
  rep(i, ps.size()) a += cross(ps[i], ps[(i + 1) % ps.size()]);
  return a / 2;
}
bool con_in_con(VP a1, VP a2) {
  int p = 0;
  rep(j, a2.size()) {
    int sum = 0, f = 0;
    rep(i, a1.size()) {
      if (isecSP(a1[i], a1[(i + 1) % a1.size()], a2[j]))
        goto L1;
      if (isecSS(a1[i], a1[(i + 1) % a1.size()], a2[j], P(1000007, 41321))) {
        sum++;
        f++;
      }
    }
    p++;
    if ((sum % 2))
      return 1;
    if ((sum % 2 == 0))
      return 0;
  L1:;
    sum = f = 0;
    rep(i, a1.size()) {
      if (isecSP(a1[i], a1[(i + 1) % a1.size()],
                 (a2[j] + a2[(j + 1) % a2.size()]) / 2.0))
        goto L;
      if (isecSS(a1[i], a1[(i + 1) % a1.size()],
                 (a2[j] + a2[(j + 1) % a2.size()]) / 2.0, P(1000007, 41321))) {
        sum++;
        f++;
      }
    }
    p++;
    if ((sum % 2))
      return 1;
    if ((sum % 2 == 0))
      return 0;
    sum = 0;
  L:;
  }
  return !p;
}
signed main() {
  vector<P> s1, s2;
  int n, m, q;
  cin >> n;
  VP V[n];
  rep(i, n) {
    cin >> m;
    D x[m], y[m];
    rep(j, m) {
      cin >> x[j] >> y[j];
      V[i].push_back(P(x[j], y[j]));
    }
    rep(j, m) {
      s1.push_back(P(x[j], y[j]));
      s2.push_back(P(x[(j + 1) % m], y[(j + 1) % m]));
    }
  }
  VP convex;
  convex.push_back(P(-200000, -200000));
  convex.push_back(P(200000, -200000));
  convex.push_back(P(200000, 200000));
  convex.push_back(P(-200000, 200000));
  Vcon v;
  divide_convex(convex, s1, s2, v);
  vector<vector<int>> a = Convex_arrangement(v);
  int cnt[v.size()] = {};
  rep(i, v.size()) {
    rep(j, n) {
      if (con_in_con(V[j], v[i])) {
        cnt[i]++;
      }
    }
  }
  rep(i, v.size()) {
    rep(j, v.size()) {
      if (i == j)
        a[i][j] = 0;
      if (a[i][j] == 1) {
        a[i][j] = abs(cnt[i] - cnt[j]);
      }
    }
  }
  rep(k, v.size()) rep(i, v.size()) rep(j, v.size()) a[i][j] =
      min(a[i][j], a[i][k] + a[k][j]);
  cin >> q;
  while (q--) {
    int A = 1e9, B = 1e9;
    D a1, a2, b1, b2;
    cin >> a1 >> a2 >> b1 >> b2;
    rep(i, v.size()) {
      if (inPolygon(v[i], P(a1, a2))) {
        A = i;
        if (inPolygon(v[i], P(a1, a2)) == 1)
          break;
      }
    }
    rep(i, v.size()) {
      if (inPolygon(v[i], P(b1, b2))) {
        B = i;
        if (inPolygon(v[i], P(b1, b2)) == 1)
          break;
      }
    }
    cout << a[A][B] << endl;
  }
}
accuracy: 1
time_ms: 40
memory_kb: 4008
final_rank: 14

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (n); i++)
typedef double D;
typedef complex<D> P;
typedef pair<P, P> L;
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7;
#define X real()
#define Y imag()
#define LE(n, m) ((n) < (m) + EPS)
#define GE(n, m) ((n) + EPS > (m))
#define EQ(n, m) (abs((n) - (m)) < EPS)
D dot(P a, P b) { return (conj(a) * b).X; }
D cross(P a, P b) { return (conj(a) * b).Y; }
int ccw(P a, P b, P c) {
  b -= a;
  c -= a;
  if (cross(b, c) > EPS)
    return +1;
  if (cross(b, c) < -EPS)
    return -1;
  if (dot(b, c) < -EPS)
    return +2;
  if (norm(b) < norm(c))
    return -2;
  return 0;
}
bool isparallel(L a, L b) {
  return cross(a.first - a.second, b.first - b.second) == 0.0;
}
bool isecLP(P a1, P a2, P b) { return abs(ccw(a1, a2, b)) != 1; }
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2 - a1, b2 - b1, 0) || isecLP(a1, b1, b2);
}
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2 - a1, b1 - a1) * cross(a2 - a1, b2 - a1) < EPS;
}
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;
}
bool isecSP(P a1, P a2, P b) {
  return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;
}
P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2 - b1, b1 - a1);
  D d2 = cross(b2 - b1, a2 - a1);
  if (EQ(d1, 0) && EQ(d2, 0))
    return a1;
  if (EQ(d2, 0))
    throw "kouten ga nai";
  return a1 + d1 / d2 * (a2 - a1);
}
namespace std {
bool operator<(const P a, const P b) {
  return a.X != b.X ? a.X < b.X : a.Y < b.Y;
}
} // namespace std
VP convexHull(VP ps) {
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2 * n);
  for (int i = 0; i < n; ch[k++] = ps[i++])
    while (k >= 2 && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0)
      --k;
  for (int i = n - 2, t = k + 1; i >= 0; ch[k++] = ps[i--])
    while (k >= t && ccw(ch[k - 2], ch[k - 1], ps[i]) <= 0)
      --k;
  ch.resize(k - 1);
  return ch;
}
bool isCcwConvex(const VP &ps) {
  int n = ps.size();
  rep(i,
      n) if (ccw(ps[i], ps[(i + 1) % n], ps[(i + 2) % n]) == -1) return false;
  return true;
}
int inConvex(P p, const VP &ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep(i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc)
      return 2;
    if (ccwc != dir)
      return 0;
  }
  return 1;
}
int inPolygon(const VP &ps, P p) {
  int n = ps.size();
  bool in = false;
  rep(i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a, b), 0) && LE(dot(a, b), 0))
      return 2;
    if (a.Y > b.Y)
      swap(a, b);
    if ((a.Y * b.Y < 0 || (a.Y * b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0))
      in = !in;
  }
  return in;
}
VP convexCut(const VP &ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i, n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1)
      ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1)
      ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP &);
bool isec_CS(const VP &ps, P a1, P a2) {
  rep(i, ps.size()) {
    if (isecLS(ps[i], ps[(i + 1) % ps.size()], a1, a2))
      return true;
  }
  return false;
}
void divide_convex(VP ps, const VP &s1, const VP &s2, Vcon &V) {
  bool flag = false;
  for (int i = 0; i < s1.size(); i++) {
    VP convex_1 = convexCut(ps, s1[i], s2[i]);
    VP convex_2 = convexCut(ps, s2[i], s1[i]);
    if (area(convex_1) < EPS || area(convex_2) < EPS)
      continue;
    divide_convex(convex_1, s1, s2, V);
    divide_convex(convex_2, s1, s2, V);
    flag = true;
    break;
  }
  if (!flag)
    V.push_back(ps);
  return;
}
bool isecCC(VP a1, VP a2) {
  rep(i, a1.size()) {
    rep(j, a2.size()) {
      P e1 = a1[i];
      P e2 = a1[(i + 1) % a1.size()];
      P e3 = a2[j];
      P e4 = a2[(j + 1) % a2.size()];
      if (abs(e1 - e3) < EPS && abs(e2 - e4) < EPS)
        return 1;
      if (abs(e1 - e4) < EPS && abs(e2 - e3) < EPS)
        return 1;
    }
  }
  return 0;
}
vector<vector<int>> Convex_arrangement(const Vcon &ps) {
  int n = ps.size();
  vector<vector<int>> V(n, vector<int>(n));
  rep(i, n) rep(j, n) V[i][j] = 1e8;
  rep(i, n) {
    for (int j = i + 1; j < n; j++) {
      if (isecCC(ps[i], ps[j])) {
        V[i][j] = 1;
        V[j][i] = 1;
      }
    }
  }
  return V;
}
D area(const VP &ps) {
  D a = 0;
  rep(i, ps.size()) a += cross(ps[i], ps[(i + 1) % ps.size()]);
  return a / 2;
}
bool con_in_con(VP a1, VP a2) {
  int p = 0;
  rep(j, a2.size()) {
    int sum = 0, f = 0;
    if (inPolygon(a1, a2[j]) == 0)
      return 0;
    rep(i, a1.size()) {
      if (isecSP(a1[i], a1[(i + 1) % a1.size()], a2[j]))
        goto L1;
      if (isecSS(a1[i], a1[(i + 1) % a1.size()], a2[j], P(1000007, 41321))) {
        sum++;
        f++;
      }
    }
    p++;
    if ((sum % 2))
      return 1;
    if ((sum % 2 == 0))
      return 0;
  L1:;
    sum = f = 0;
    rep(i, a1.size()) {
      if (isecSP(a1[i], a1[(i + 1) % a1.size()],
                 (a2[j] + a2[(j + 1) % a2.size()]) / 2.0))
        goto L;
      if (isecSS(a1[i], a1[(i + 1) % a1.size()],
                 (a2[j] + a2[(j + 1) % a2.size()]) / 2.0, P(1000007, 41321))) {
        sum++;
        f++;
      }
    }
    p++;
    if ((sum % 2))
      return 1;
    if ((sum % 2 == 0))
      return 0;
    sum = 0;
  L:;
  }
  return !p;
}
signed main() {
  vector<P> s1, s2;
  int n, m, q;
  cin >> n;
  VP V[n];
  rep(i, n) {
    cin >> m;
    D x[m], y[m];
    rep(j, m) {
      cin >> x[j] >> y[j];
      V[i].push_back(P(x[j], y[j]));
    }
    rep(j, m) {
      s1.push_back(P(x[j], y[j]));
      s2.push_back(P(x[(j + 1) % m], y[(j + 1) % m]));
    }
  }
  VP convex;
  convex.push_back(P(-200000, -200000));
  convex.push_back(P(200000, -200000));
  convex.push_back(P(200000, 200000));
  convex.push_back(P(-200000, 200000));
  Vcon v;
  divide_convex(convex, s1, s2, v);
  vector<vector<int>> a = Convex_arrangement(v);
  int cnt[v.size()] = {};
  rep(i, v.size()) {
    rep(j, n) {
      if (con_in_con(V[j], v[i])) {
        cnt[i]++;
      }
    }
  }
  rep(i, v.size()) {
    rep(j, v.size()) {
      if (i == j)
        a[i][j] = 0;
      if (a[i][j] == 1) {
        a[i][j] = abs(cnt[i] - cnt[j]);
      }
    }
  }
  rep(k, v.size()) rep(i, v.size()) rep(j, v.size()) a[i][j] =
      min(a[i][j], a[i][k] + a[k][j]);
  cin >> q;
  while (q--) {
    int A = 1e9, B = 1e9;
    D a1, a2, b1, b2;
    cin >> a1 >> a2 >> b1 >> b2;
    rep(i, v.size()) {
      if (inPolygon(v[i], P(a1, a2))) {
        A = i;
        if (inPolygon(v[i], P(a1, a2)) == 1)
          break;
      }
    }
    rep(i, v.size()) {
      if (inPolygon(v[i], P(b1, b2))) {
        B = i;
        if (inPolygon(v[i], P(b1, b2)) == 1)
          break;
      }
    }
    cout << a[A][B] << endl;
  }
}
accuracy: 1
time_ms: 40
memory_kb: 3940
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;
const double eps = 0.000001;
int sign(double x){
  if (x > eps){
    return 1;
  } else if (x < -eps){
    return -1;
  } else {
    return 0;
  }
}
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator +(point P){
    return point(x + P.x, y + P.y);
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
  point operator *(double k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dist(point P, point Q){
  return abs(Q - P);
}
point rotate(point P, double t){
  return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t));
}
double cross(point P, point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(){
  }
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool is_parallel(line L1, line L2){
  return abs(cross(vec(L1), vec(L2))) < eps;
}
bool segment_line_intersect(line L1, line L2){
  return sign(cross(L1.A - L2.A, vec(L2))) * sign(cross(L1.B - L2.A, vec(L2))) <= 0;
}
bool segment_intersect(line L1, line L2){
  return segment_line_intersect(L1, L2) && segment_line_intersect(L2, L1);
}
point line_intersection(line L1, line L2){
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}
vector<double> unique_eps(vector<double> A){
  int N = A.size();
  sort(A.begin(), A.end());
  vector<double> A2;
  A2.push_back(A[0]);
  for (int i = 1; i < N; i++){
    if (A[i] - A[i - 1] > eps){
      A2.push_back(A[i]);
    }
  }
  return A2;
}
template <typename T>
struct slab_decomposition{
  function<T(T, T)> f;
  T e;
  vector<pair<line, T>> S;
  vector<double> X;
  int N;
  vector<vector<double>> Y;
  vector<int> P;
  vector<vector<tuple<int, int, T>>> E;
  slab_decomposition(function<T(T, T)> f, T e): f(f), e(e){
  }
  void add_segment(line s, T x){
    S.push_back(make_pair(s, x));
  }
  vector<vector<pair<T, int>>> build(){
    int M = S.size();
    for (int i = 0; i < M; i++){
      if (S[i].first.A.x > S[i].first.B.x){
        swap(S[i].first.A, S[i].first.B);
      }
    }
    for (int i = 0; i < M; i++){
      X.push_back(S[i].first.A.x);
      X.push_back(S[i].first.B.x);
    }
    for (int i = 0; i < M; i++){
      for (int j = i + 1; j < M; j++){
        if (!is_parallel(S[i].first, S[j].first) && segment_intersect(S[i].first, S[j].first)){
          X.push_back(line_intersection(S[i].first, S[j].first).x);
        }
      }
    }
    X = unique_eps(X);
    N = X.size();
    vector<int> L(M), R(M);
    for (int i = 0; i < M; i++){
      L[i] = lower_bound(X.begin(), X.end(), S[i].first.A.x - eps) - X.begin();
      R[i] = lower_bound(X.begin(), X.end(), S[i].first.B.x - eps) - X.begin();
    }
    vector<vector<double>> A(M, vector<double>(N));
    Y.resize(N);
    for (int i = 0; i < M; i++){
      for (int j = L[i]; j <= R[i]; j++){
        A[i][j] = line_intersection(S[i].first, line(point(X[j], 0), point(X[j], 1))).y;
        Y[j].push_back(A[i][j]);
      }
    }
    for (int i = 0; i < N; i++){
      Y[i] = unique_eps(Y[i]);
    }
    vector<vector<int>> B(M, vector<int>(N));
    for (int i = 0; i < M; i++){
      for (int j = L[i]; j <= R[i]; j++){
        B[i][j] = lower_bound(Y[j].begin(), Y[j].end(), A[i][j] - eps) - Y[j].begin();
      }
    }
    E.resize(N - 1);
    for (int i = 0; i < N - 1; i++){
      E[i].push_back(make_tuple(-1, -1, e));
    }
    for (int i = 0; i < M; i++){
      for (int j = L[i]; j < R[i]; j++){
        E[j].push_back(make_tuple(B[i][j], B[i][j + 1], S[i].second));
      }
    }
    for (int i = 0; i < N - 1; i++){
      E[i].push_back(make_tuple(Y[i].size(), Y[i + 1].size(), e));
    }
    for (int i = 0; i < N - 1; i++){
      sort(E[i].begin(), E[i].end());
      if (!E[i].empty()){
        vector<tuple<int, int, T>> E2;
        int cnt = E[i].size();
        E2.push_back(E[i][0]);
        for (int j = 1; j < cnt; j++){
          if (get<0>(E[i][j - 1]) == get<0>(E[i][j]) && get<1>(E[i][j - 1]) == get<1>(E[i][j])){
            get<2>(E2.back()) = f(get<2>(E2.back()), get<2>(E[i][j]));
          } else {
            E2.push_back(E[i][j]);
          }
        }
        E[i] = E2;
      }
    }
    P.resize(N + 2);
    P[0] = 0;
    P[1] = 1;
    for (int i = 0; i < N - 1; i++){
      P[i + 2] = P[i + 1] + E[i].size() - 1;
    }
    P[N + 1] = P[N] + 1;
    vector<vector<int>> idL(N), idR(N);
    for (int i = 0; i < N; i++){
      idL[i] = vector<int>(Y[i].size() + 1);
      idR[i] = vector<int>(Y[i].size() + 1);
    }
    idL[0] = vector<int>(Y[0].size() + 1, 0);
    for (int i = 0; i < N - 1; i++){
      int cnt = E[i].size();
      for (int j = 0; j < cnt - 1; j++){
        for (int k = get<0>(E[i][j]) + 1; k <= get<0>(E[i][j + 1]); k++){
          idR[i][k] = P[i + 1] + j;
        }
        for (int k = get<1>(E[i][j]) + 1; k <= get<1>(E[i][j + 1]); k++){
          idL[i + 1][k] = P[i + 1] + j;
        }
      }
    }
    idR[N - 1] = vector<int>(Y[N - 1].size() + 1, P[N]);
    int V = P[N + 1];
    vector<vector<pair<T, int>>> G(V);
    for (int i = 0; i <= N; i++){
      for (int j = P[i]; j < P[i + 1] - 1; j++){
        G[j].push_back(make_pair(get<2>(E[i - 1][j - P[i] + 1]), j + 1));
        G[j + 1].push_back(make_pair(get<2>(E[i - 1][j - P[i] + 1]), j));
      }
    }
    for (int i = 0; i < N; i++){
      for (int j = 0; j <= Y[i].size(); j++){
        G[idL[i][j]].push_back(make_pair(e, idR[i][j]));
        G[idR[i][j]].push_back(make_pair(e, idL[i][j]));
      }
    }
    return G;
  }
  int find(point p){
    int x = lower_bound(X.begin(), X.end(), p.x - eps) - X.begin();
    if (x == 0){
      return 0;
    } else if (x == N){
      return P[N];
    } else {
      int ans = P[x];
      for (int i = 1; i < E[x - 1].size() - 1; i++){
        point A(X[x - 1], Y[x - 1][get<0>(E[x - 1][i])]);
        point B(X[x], Y[x][get<1>(E[x - 1][i])]);
        if (cross(B - A, p - A) > 0){
          ans++;
        }
      }
      return ans;
    }
  }
  vector<double> area(){
    int V = P[N + 1];
    vector<double> ans(V, -1);
    for (int i = 0; i < N - 1; i++){
      for(int j = 1; j < E[i].size() - 2; j++){
        double l = Y[i][get<0>(E[i][j + 1])] - Y[i][get<0>(E[i][j])];
        double r = Y[i + 1][get<1>(E[i][j + 1])] - Y[i + 1][get<1>(E[i][j])];
        ans[P[i + 1] + j] = (l + r) * (X[i + 1] - X[i]) / 2;
      }
    }
    return ans;
  }
  vector<double> length(){
    int V = P[N + 1];
    vector<double> ans(V, -1);
    for (int i = 0; i < N - 1; i++){
      for(int j = 1; j < E[i].size() - 1; j++){
        double l = Y[i][get<0>(E[i][j])];
        double r = Y[i + 1][get<1>(E[i][j])];
        ans[P[i + 1] + j - 1] = hypot(X[i + 1] - X[i], r - l);
      }
    }
    return ans;
  }
};
int main(){
  cout << fixed << setprecision(20);
  int N;
  cin >> N;
  vector<int> C(N);
  vector<vector<point>> P(N);
  for (int i = 0; i < N; i++){
    cin >> C[i];
    P[i].resize(C[i]);
    for (int j = 0; j < C[i]; j++){
      cin >> P[i][j].x >> P[i][j].y;
    }
  }
  int Q;
  cin >> Q;
  vector<point> S(Q), T(Q);
  for (int i = 0; i < Q; i++){
    cin >> S[i].x >> S[i].y >> T[i].x >> T[i].y;
  }
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      P[i][j] = rotate(P[i][j], 1);
    }
  }
  for (int i = 0; i < Q; i++){
    S[i] = rotate(S[i], 1);
    T[i] = rotate(T[i], 1);
  }
  slab_decomposition<int> D(bit_xor<int>(), 0);
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      D.add_segment(line(P[i][j], P[i][(j + 1) % C[i]]), 1 << i);
    }
  }
  vector<vector<pair<int, int>>> E = D.build();
  int V = E.size();
  vector<int> h(V, -1);
  h[0] = 0;
  queue<int> q;
  q.push(0);
  while (!q.empty()){
    int v = q.front();
    q.pop();
    for (pair<int, int> e : E[v]){
      int w = e.second;
      if (h[w] == -1){
        h[w] = h[v] ^ e.first;
        q.push(w);
      }
    }
  }
  for (int i = 0; i < Q; i++){
    int s = D.find(S[i]);
    int t = D.find(T[i]);
    vector<int> d(V, -1);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, s));
    while (!pq.empty()){
      int c = pq.top().first;
      int v = pq.top().second;
      pq.pop();
      if (d[v] == -1){
        d[v] = c;
        for (pair<int, int> e : E[v]){
          int w = e.second;
          if (d[w] == -1){
            pq.push(make_pair(c + abs(__builtin_popcount(h[v]) - __builtin_popcount(h[w])), w));
          }
        }
      }
    }
    cout << d[t] << endl;
  }
}
accuracy: 1
time_ms: 20
memory_kb: 3904
final_rank: 5

code_snippet: #include <bits/stdc++.h>
using namespace std;
const double eps = 0.000000001;
const double INF = 10000;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator +(point P){
    return point(x + P.x, y + P.y);
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
  point operator *(double k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
point rotate(point P, double t){
  return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t));
}
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dot(point P, point Q){
  return P.x * Q.x + P.y * Q.y;
}
double cross(point P, point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool point_on_line(point P, line L){
  return abs(cross(vec(L), P - L.A)) < eps;
}
bool is_parallel(line L1, line L2){
  return abs(cross(vec(L1), vec(L2))) < eps;
}
point line_intersection(line L1, line L2){
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}
bool point_on_segment(point P, line L){
  return point_on_line(P, L) && dot(P - L.A, vec(L)) > 0 && dot(P - L.B, vec(L)) < 0;
}
bool point_in_polygon(point P, vector<point> A){
  int N = A.size();
  line L1(P, point(P.x, INF));
  int cnt = 0;
  for (int i = 0; i < N; i++){
    int j = (i + 1) % N;
    line L2(A[i], A[j]);
    point X = line_intersection(L1, L2);
    if (point_on_segment(X, L1) && point_on_segment(X, L2)){
      cnt++;
    }
  }
  return cnt % 2 == 1;
}
int main(){
  int N;
  cin >> N;
  vector<int> C(N);
  vector<vector<point>> A(N);
  for (int i = 0; i < N; i++){
    cin >> C[i];
    A[i].resize(C[i]);
    for (int j = 0; j < C[i]; j++){
      cin >> A[i][j].x >> A[i][j].y;
    }
  }
  int Q;
  cin >> Q;
  vector<point> S(Q), T(Q);
  for (int i = 0; i < Q; i++){
    cin >> S[i].x >> S[i].y >> T[i].x >> T[i].y;
  }
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      A[i][j] = rotate(A[i][j], 0.01);
    }
  }
  for (int i = 0; i < Q; i++){
    S[i] = rotate(S[i], 0.01);
    T[i] = rotate(T[i], 0.01);
  }
  vector<line> L;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      int k = (j + 1) % C[i];
      L.push_back(line(A[i][j], A[i][k]));
    }
  }
  int Lcnt = L.size();
  vector<double> X;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      X.push_back(A[i][j].x);
    }
  }
  for (int i = 0; i < Q; i++){
    X.push_back(S[i].x);
    X.push_back(T[i].x);
  }
  for (int i = 0; i < Lcnt; i++){
    for (int j = i + 1; j < Lcnt; j++){
      if (!is_parallel(L[i], L[j])){
        point P = line_intersection(L[i], L[j]);
        if (point_on_segment(P, L[i]) && point_on_segment(P, L[j])){
          X.push_back(P.x);
        }
      }
    }
  }
  sort(X.begin(), X.end());
  vector<double> X2;
  X2.push_back(X[0]);
  for (int i = 1; i < X.size(); i++){
    if (X[i] - X[i - 1] > eps){
      X2.push_back(X[i]);
    }
  }
  X = X2;
  int M = X.size();
  vector<vector<double>> Y(M);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j <= r; j++){
      point P = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      Y[j].push_back(P.y);
    }
  }
  for (int i = 0; i < M; i++){
    if (!Y[i].empty()){
      int cnt = Y[i].size();
      sort(Y[i].begin(), Y[i].end());
      vector<double> Y2;
      Y2.push_back(Y[i][0]);
      for (int j = 1; j < cnt; j++){
        if (Y[i][j] - Y[i][j - 1] > eps){
          Y2.push_back(Y[i][j]);
        }
      }
      Y[i] = Y2;
    }
  }
  vector<vector<pair<int, int>>> L2(M + 1);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j < r; j++){
      point P1 = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      point P2 = line_intersection(L[i], line(point(X[j + 1], 0), point(X[j + 1], 1)));
      int p1 = lower_bound(Y[j].begin(), Y[j].end(), P1.y - eps) - Y[j].begin();
      int p2 = lower_bound(Y[j + 1].begin(), Y[j + 1].end(), P2.y - eps) - Y[j + 1].begin();
      L2[j + 1].push_back(make_pair(p1, p2));
    }
  }
  for (int i = 0; i <= M; i++){
    sort(L2[i].begin(), L2[i].end());
    L2[i].erase(unique(L2[i].begin(), L2[i].end()), L2[i].end());
  }
  vector<int> sum(M + 2);
  sum[0] = 0;
  for (int i = 0; i <= M; i++){
    sum[i + 1] = sum[i] + L2[i].size() + 1;
  }
  int V = sum[M + 1];
  vector<int> con(V, 0);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 1; j < cnt; j++){
      double px = (X[i - 1] + X[i]) / 2;
      double py = (Y[i - 1][L2[i][j - 1].first] + Y[i - 1][L2[i][j].first] + Y[i][L2[i][j - 1].second] + Y[i][L2[i][j].second]) / 4;
      point P(px, py);
      for (int k = 0; k < N; k++){
        if (point_in_polygon(P, A[k])){
          con[sum[i] + j]++;
        }
      }
    }
  }
  vector<vector<pair<int, int>>> E(V);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 0; j < cnt; j++){
      int u = sum[i] + j;
      int v = sum[i] + j + 1;
      int c = abs(con[u] - con[v]);
      E[u].push_back(make_pair(c, v));
      E[v].push_back(make_pair(c, u));
    }
  }
  for (int i = 0; i < M; i++){
    int cnt = Y[i].size();
    vector<int> id1(cnt + 1), id2(cnt + 1);
    int cnt1 = L2[i].size(), cnt2 = L2[i + 1].size();
    if (cnt1 == 0){
      id1 = vector<int>(cnt + 1, sum[i]);
    } else {
      for (int j = 0; j <= L2[i][0].second; j++){
        id1[j] = sum[i];
      }
      for (int j = 0; j < cnt1 - 1; j++){
        for (int k = L2[i][j].second + 1; k < L2[i][j + 1].second + 1; k++){
          id1[k] = sum[i] + j + 1;
        }
      }
      for (int j = L2[i][cnt1 - 1].second + 1; j <= cnt; j++){
        id1[j] = sum[i] + cnt1;
      }
    }
    if (cnt2 == 0){
      id2 = vector<int>(cnt + 1, sum[i + 1]);
    } else {
      for (int j = 0; j <= L2[i + 1][0].first; j++){
        id2[j] = sum[i + 1];
      }
      for (int j = 0; j < cnt2 - 1; j++){
        for (int k = L2[i + 1][j].first + 1; k <= L2[i + 1][j + 1].first; k++){
          id2[k] = sum[i + 1] + j + 1;
        }
      }
      for (int j = L2[i + 1][cnt2 - 1].first + 1; j <= cnt; j++){
        id2[j] = sum[i + 1] + cnt2;
      }
    }
    for (int j = 0; j <= cnt; j++){
      int c = abs(con[id1[j]] - con[id2[j]]);
      E[id1[j]].push_back(make_pair(c, id2[j]));
      E[id2[j]].push_back(make_pair(c, id1[j]));
    }
  }
  for (int i = 0; i < Q; i++){
    int xs = lower_bound(X.begin(), X.end(), S[i].x - eps) - X.begin();
    int xt = lower_bound(X.begin(), X.end(), T[i].x - eps) - X.begin();
    int s;
    int cnt1 = L2[xs].size();
    if (cnt1 == 0){
      s = sum[xs];
    } else if (S[i].y < Y[xs][0]){
      s = sum[xs];
    } else if (S[i].y > Y[xs][Y[xs].size() - 1]){
      s = sum[xs] + cnt1;
    } else {
      for (int j = 0; j < cnt1 - 1; j++){
        if (Y[xs][L2[xs][j].second] < S[i].y && S[i].y < Y[xs][L2[xs][j + 1].second]){
          s = sum[xs] + j + 1;
        }
      }
    }
    int t;
    int cnt2 = L2[xt].size();
    if (cnt2 == 0){
      t = sum[xt];
    } else if (T[i].y < Y[xt][0]){
      t = sum[xt];
    } else if (T[i].y > Y[xt][Y[xt].size() - 1]){
      t = sum[xt] + cnt2;
    } else {
      for (int j = 0; j < cnt2 - 1; j++){
        if (Y[xt][L2[xt][j].second] < T[i].y && T[i].y < Y[xt][L2[xt][j + 1].second]){
          t = sum[xt] + j + 1;
        }
      }
    }
    vector<int> d(V, -1);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, s));
    while (!pq.empty()){
      int c = pq.top().first;
      int v = pq.top().second;
      pq.pop();
      if (d[v] == -1){
        d[v] = c;
        for (auto P : E[v]){
          int w = P.second;
          pq.push(make_pair(c + P.first, w));
        }
      }
    }
    cout << d[t] << endl;
  }
}
accuracy: 1
time_ms: 30
memory_kb: 3936
final_rank: 7

code_snippet: #include <bits/stdc++.h>
using namespace std;
const double eps = 0.000000001;
const double INF = 10000;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator +(point P){
    return point(x + P.x, y + P.y);
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
  point operator *(double k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
point rotate(point P, double t){
  return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t));
}
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dot(point P, point Q){
  return P.x * Q.x + P.y * Q.y;
}
double cross(point P, point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool point_on_line(point P, line L){
  return abs(cross(vec(L), P - L.A)) < eps;
}
bool is_parallel(line L1, line L2){
  return abs(cross(vec(L1), vec(L2))) < eps;
}
bool is_same(line L1, line L2){
  return point_on_line(L2.A, L1) && point_on_line(L2.B, L1);
}
point line_intersection(line L1, line L2){
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}
bool point_on_segment(point P, line L){
  return point_on_line(P, L) && dot(P - L.A, vec(L)) > 0 && dot(P - L.B, vec(L)) < 0;
}
bool point_in_polygon(point P, vector<point> A){
  int N = A.size();
  line L1(P, point(P.x, INF));
  int cnt = 0;
  for (int i = 0; i < N; i++){
    int j = (i + 1) % N;
    line L2(A[i], A[j]);
    point X = line_intersection(L1, L2);
    if (point_on_segment(X, L1) && point_on_segment(X, L2)){
      cnt++;
    }
  }
  return cnt % 2 == 1;
}
int main(){
  int N;
  cin >> N;
  vector<int> C(N);
  vector<vector<point>> A(N);
  for (int i = 0; i < N; i++){
    cin >> C[i];
    A[i].resize(C[i]);
    for (int j = 0; j < C[i]; j++){
      cin >> A[i][j].x >> A[i][j].y;
    }
  }
  int Q;
  cin >> Q;
  vector<point> S(Q), T(Q);
  for (int i = 0; i < Q; i++){
    cin >> S[i].x >> S[i].y >> T[i].x >> T[i].y;
  }
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      A[i][j] = rotate(A[i][j], 0.01);
    }
  }
  for (int i = 0; i < Q; i++){
    S[i] = rotate(S[i], 0.01);
    T[i] = rotate(T[i], 0.01);
  }
  vector<line> L;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      int k = (j + 1) % C[i];
      L.push_back(line(A[i][j], A[i][k]));
    }
  }
  int Lcnt = L.size();
  vector<double> X;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      X.push_back(A[i][j].x);
    }
  }
  for (int i = 0; i < Q; i++){
    X.push_back(S[i].x);
    X.push_back(T[i].x);
  }
  for (int i = 0; i < Lcnt; i++){
    for (int j = i + 1; j < Lcnt; j++){
      if (!is_parallel(L[i], L[j])){
        point P = line_intersection(L[i], L[j]);
        if (point_on_segment(P, L[i]) && point_on_segment(P, L[j])){
          X.push_back(P.x);
        }
      }
    }
  }
  sort(X.begin(), X.end());
  vector<double> X2;
  X2.push_back(X[0]);
  for (int i = 1; i < X.size(); i++){
    if (X[i] - X[i - 1] > eps){
      X2.push_back(X[i]);
    }
  }
  X = X2;
  int M = X.size();
  vector<vector<double>> Y(M);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j <= r; j++){
      point P = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      Y[j].push_back(P.y);
    }
  }
  for (int i = 0; i < M; i++){
    if (!Y[i].empty()){
      int cnt = Y[i].size();
      sort(Y[i].begin(), Y[i].end());
      vector<double> Y2;
      Y2.push_back(Y[i][0]);
      for (int j = 1; j < cnt; j++){
        if (Y[i][j] - Y[i][j - 1] > eps){
          Y2.push_back(Y[i][j]);
        }
      }
      Y[i] = Y2;
    }
  }
  vector<vector<pair<int, int>>> L2(M + 1);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j < r; j++){
      point P1 = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      point P2 = line_intersection(L[i], line(point(X[j + 1], 0), point(X[j + 1], 1)));
      int p1 = lower_bound(Y[j].begin(), Y[j].end(), P1.y - eps) - Y[j].begin();
      int p2 = lower_bound(Y[j + 1].begin(), Y[j + 1].end(), P2.y - eps) - Y[j + 1].begin();
      L2[j + 1].push_back(make_pair(p1, p2));
    }
  }
  for (int i = 0; i <= M; i++){
    sort(L2[i].begin(), L2[i].end());
    L2[i].erase(unique(L2[i].begin(), L2[i].end()), L2[i].end());
  }
  vector<int> sum(M + 2);
  sum[0] = 0;
  for (int i = 0; i <= M; i++){
    sum[i + 1] = sum[i] + L2[i].size() + 1;
  }
  int V = sum[M + 1];
  vector<int> con(V, 0);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 1; j < cnt; j++){
      double px = (X[i - 1] + X[i]) / 2;
      double py = (Y[i - 1][L2[i][j - 1].first] + Y[i - 1][L2[i][j].first] + Y[i][L2[i][j - 1].second] + Y[i][L2[i][j].second]) / 4;
      point P(px, py);
      for (int k = 0; k < N; k++){
        if (point_in_polygon(P, A[k])){
          con[sum[i] + j]++;
        }
      }
    }
  }
  vector<vector<pair<int, int>>> E(V);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 0; j < cnt; j++){
      int u = sum[i] + j;
      int v = sum[i] + j + 1;
      int c = abs(con[u] - con[v]);
      E[u].push_back(make_pair(c, v));
      E[v].push_back(make_pair(c, u));
    }
  }
  for (int i = 0; i < M; i++){
    int cnt = Y[i].size();
    vector<int> id1(cnt + 1), id2(cnt + 1);
    int cnt1 = L2[i].size(), cnt2 = L2[i + 1].size();
    if (cnt1 == 0){
      id1 = vector<int>(cnt + 1, sum[i]);
    } else {
      for (int j = 0; j <= L2[i][0].second; j++){
        id1[j] = sum[i];
      }
      for (int j = 0; j < cnt1 - 1; j++){
        for (int k = L2[i][j].second + 1; k < L2[i][j + 1].second + 1; k++){
          id1[k] = sum[i] + j + 1;
        }
      }
      for (int j = L2[i][cnt1 - 1].second + 1; j <= cnt; j++){
        id1[j] = sum[i] + cnt1;
      }
    }
    if (cnt2 == 0){
      id2 = vector<int>(cnt + 1, sum[i + 1]);
    } else {
      for (int j = 0; j <= L2[i + 1][0].first; j++){
        id2[j] = sum[i + 1];
      }
      for (int j = 0; j < cnt2 - 1; j++){
        for (int k = L2[i + 1][j].first + 1; k <= L2[i + 1][j + 1].first; k++){
          id2[k] = sum[i + 1] + j + 1;
        }
      }
      for (int j = L2[i + 1][cnt2 - 1].first + 1; j <= cnt; j++){
        id2[j] = sum[i + 1] + cnt2;
      }
    }
    for (int j = 0; j <= cnt; j++){
      int c = abs(con[id1[j]] - con[id2[j]]);
      E[id1[j]].push_back(make_pair(c, id2[j]));
      E[id2[j]].push_back(make_pair(c, id1[j]));
    }
  }
  for (int i = 0; i < Q; i++){
    int xs = lower_bound(X.begin(), X.end(), S[i].x - eps) - X.begin();
    int xt = lower_bound(X.begin(), X.end(), T[i].x - eps) - X.begin();
    int s;
    int cnt1 = L2[xs].size();
    if (cnt1 == 0){
      s = sum[xs];
    } else if (S[i].y < Y[xs][0]){
      s = sum[xs];
    } else if (S[i].y > Y[xs][Y[xs].size() - 1]){
      s = sum[xs] + cnt1;
    } else {
      for (int j = 0; j < cnt1 - 1; j++){
        if (Y[xs][L2[xs][j].second] < S[i].y && S[i].y < Y[xs][L2[xs][j + 1].second]){
          s = sum[xs] + j + 1;
        }
      }
    }
    int t;
    int cnt2 = L2[xt].size();
    if (cnt2 == 0){
      t = sum[xt];
    } else if (T[i].y < Y[xt][0]){
      t = sum[xt];
    } else if (T[i].y > Y[xt][Y[xt].size() - 1]){
      t = sum[xt] + cnt2;
    } else {
      for (int j = 0; j < cnt2 - 1; j++){
        if (Y[xt][L2[xt][j].second] < T[i].y && T[i].y < Y[xt][L2[xt][j + 1].second]){
          t = sum[xt] + j + 1;
        }
      }
    }
    vector<int> d(V, -1);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, s));
    while (!pq.empty()){
      int c = pq.top().first;
      int v = pq.top().second;
      pq.pop();
      if (d[v] == -1){
        d[v] = c;
        for (auto P : E[v]){
          int w = P.second;
          pq.push(make_pair(c + P.first, w));
        }
      }
    }
    cout << d[t] << endl;
  }
}
accuracy: 1
time_ms: 30
memory_kb: 3912
final_rank: 6

code_snippet: #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;
const double eps = 0.000000001;
const double INF = 10000;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator +(point P){
    return point(x + P.x, y + P.y);
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
  point operator *(double k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
point rotate(point P, double t){
  return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t));
}
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dot(point P, point Q){
  return P.x * Q.x + P.y * Q.y;
}
double cross(point P,  point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool point_on_line(point P, line L){
  return abs(cross(vec(L), P - L.A)) < eps;
}
bool is_parallel(line L1, line L2){
  return abs(cross(vec(L1), vec(L2))) < eps;
}
bool is_same(line L1, line L2){
  return point_on_line(L2.A, L1) && point_on_line(L2.B, L1);
}
point line_intersection(line L1, line L2){
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}
bool point_on_segment(point P, line L){
  return point_on_line(P, L) && dot(P - L.A, vec(L)) > -eps && dot(P - L.B, vec(L)) < eps;
}
bool point_in_polygon(point P, vector<point> A){
  int N = A.size();
  line L1(P, point(P.x, INF));
  int cnt = 0;
  for (int i = 0; i < N; i++){
    int j = (i + 1) % N;
    line L2(A[i], A[j]);
    point X = line_intersection(L1, L2);
    if (point_on_segment(X, L1) && point_on_segment(X, L2)){
      cnt++;
    }
  }
  return cnt % 2 == 1;
}
int main(){
  int N;
  cin >> N;
  vector<int> C(N);
  vector<vector<point>> A(N);
  for (int i = 0; i < N; i++){
    cin >> C[i];
    A[i].resize(C[i]);
    for (int j = 0; j < C[i]; j++){
      cin >> A[i][j].x >> A[i][j].y;
    }
  }
  int Q;
  cin >> Q;
  vector<point> S(Q), T(Q);
  for (int i = 0; i < Q; i++){
    cin >> S[i].x >> S[i].y >> T[i].x >> T[i].y;
  }
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      A[i][j] = rotate(A[i][j], 0.01);
    }
  }
  for (int i = 0; i < Q; i++){
    S[i] = rotate(S[i], 0.01);
    T[i] = rotate(T[i], 0.01);
  }
  vector<line> L;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      int k = (j + 1) % C[i];
      L.push_back(line(A[i][j], A[i][k]));
    }
  }
  int Lcnt = L.size();
  vector<double> X;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      X.push_back(A[i][j].x);
    }
  }
  for (int i = 0; i < Q; i++){
    X.push_back(S[i].x);
    X.push_back(T[i].x);
  }
  for (int i = 0; i < Lcnt; i++){
    for (int j = i + 1; j < Lcnt; j++){
      if (!is_parallel(L[i], L[j])){
        point P = line_intersection(L[i], L[j]);
        if (point_on_segment(P, L[i]) && point_on_segment(P, L[j])){
          X.push_back(P.x);
        }
      }
    }
  }
  sort(X.begin(), X.end());
  vector<double> X2;
  X2.push_back(X[0]);
  for (int i = 1; i < X.size(); i++){
    if (X[i] - X[i - 1] > eps){
      X2.push_back(X[i]);
    }
  }
  X = X2;
  int M = X.size();
  vector<vector<double>> Y(M);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j <= r; j++){
      point P = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      Y[j].push_back(P.y);
    }
  }
  for (int i = 0; i < M; i++){
    if (!Y[i].empty()){
      int cnt = Y[i].size();
      sort(Y[i].begin(), Y[i].end());
      vector<double> Y2;
      Y2.push_back(Y[i][0]);
      for (int j = 1; j < cnt; j++){
        if (Y[i][j] - Y[i][j - 1] > eps){
          Y2.push_back(Y[i][j]);
        }
      }
      Y[i] = Y2;
    }
  }
  vector<vector<pair<int, int>>> L2(M + 1);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j < r; j++){
      point P1 = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      point P2 = line_intersection(L[i], line(point(X[j + 1], 0), point(X[j + 1], 1)));
      int p1 = lower_bound(Y[j].begin(), Y[j].end(), P1.y - eps) - Y[j].begin();
      int p2 = lower_bound(Y[j + 1].begin(), Y[j + 1].end(), P2.y - eps) - Y[j + 1].begin();
      L2[j + 1].push_back(make_pair(p1, p2));
    }
  }
  for (int i = 0; i <= M; i++){
    sort(L2[i].begin(), L2[i].end());
    L2[i].erase(unique(L2[i].begin(), L2[i].end()), L2[i].end());

    int cnt = L2[i].size();
    for (int j = 0; j < cnt - 1; j++){
      assert(L2[i][j].second <= L2[i][j + 1].second);
    }
  }
  vector<int> sum(M + 2);
  sum[0] = 0;
  for (int i = 0; i <= M; i++){
    sum[i + 1] = sum[i] + L2[i].size() + 1;
  }
  int V = sum[M + 1];
  vector<int> con(V, 0);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 1; j < cnt; j++){
      double px = (X[i - 1] + X[i]) / 2;
      double py = (Y[i - 1][L2[i][j - 1].first] + Y[i - 1][L2[i][j].first] + Y[i][L2[i][j - 1].second] + Y[i][L2[i][j].second]) / 4;
      point P(px, py);
      for (int k = 0; k < N; k++){
        if (point_in_polygon(P, A[k])){
          con[sum[i] + j]++;
        }
      }
    }
  }
  vector<vector<pair<int, int>>> E(V);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 0; j < cnt - 1; j++){
      int u = sum[i] + j;
      int v = sum[i] + j + 1;
      int c = abs(con[u] - con[v]);
      E[u].push_back(make_pair(c, v));
      E[v].push_back(make_pair(c, u));
    }
  }
  for (int i = 0; i < M; i++){
    int cnt = Y[i].size();
    vector<int> id1(cnt + 1), id2(cnt + 1);
    int cnt1 = L2[i].size(), cnt2 = L2[i + 1].size();
    if (cnt1 == 0){
      id1 = vector<int>(cnt + 1, sum[i]);
    } else {
      for (int j = 0; j <= L2[i][0].second; j++){
        id1[j] = sum[i];
      }
      for (int j = 0; j < cnt1 - 1; j++){
        for (int k = L2[i][j].second + 1; k < L2[i][j + 1].second + 1; k++){
          id1[k] = sum[i] + j + 1;
        }
      }
      for (int j = L2[i][cnt1 - 1].second + 1; j <= cnt; j++){
        id1[j] = sum[i] + cnt1;
      }
    }
    if (cnt2 == 0){
      id2 = vector<int>(cnt + 1, sum[i + 1]);
    } else {
      for (int j = 0; j <= L2[i + 1][0].first; j++){
        id2[j] = sum[i + 1];
      }
      for (int j = 0; j < cnt2 - 1; j++){
        for (int k = L2[i + 1][j].first + 1; k <= L2[i + 1][j + 1].first; k++){
          id2[k] = sum[i + 1] + j + 1;
        }
      }
      for (int j = L2[i + 1][cnt2 - 1].first + 1; j <= cnt; j++){
        id2[j] = sum[i + 1] + cnt2;
      }
    }
    for (int j = 0; j <= cnt; j++){
      int c = abs(con[id1[j]] - con[id2[j]]);
      E[id1[j]].push_back(make_pair(c, id2[j]));
      E[id2[j]].push_back(make_pair(c, id1[j]));
    }
  }
  for (int i = 0; i < Q; i++){
    int xs = lower_bound(X.begin(), X.end(), S[i].x - eps) - X.begin();
    int xt = lower_bound(X.begin(), X.end(), T[i].x - eps) - X.begin();
    int s;
    int cnt1 = L2[xs].size();
    if (cnt1 == 0){
      s = sum[xs];
    } else if (S[i].y < Y[xs][0]){
      s = sum[xs];
    } else if (S[i].y > Y[xs][Y[xs].size() - 1]){
      s = sum[xs] + cnt1;
    } else {
      for (int j = 0; j < cnt1 - 1; j++){
        if (Y[xs][L2[xs][j].second] < S[i].y && S[i].y < Y[xs][L2[xs][j + 1].second]){
          s = sum[xs] + j + 1;
        }
      }
    }
    int t;
    int cnt2 = L2[xt].size();
    if (cnt2 == 0){
      t = sum[xt];
    } else if (T[i].y < Y[xt][0]){
      t = sum[xt];
    } else if (T[i].y > Y[xt][Y[xt].size() - 1]){
      t = sum[xt] + cnt2;
    } else {
      for (int j = 0; j < cnt2 - 1; j++){
        if (Y[xt][L2[xt][j].second] < T[i].y && T[i].y < Y[xt][L2[xt][j + 1].second]){
          t = sum[xt] + j + 1;
        }
      }
    }
    vector<int> d(V, -1);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, s));
    while (!pq.empty()){
      int c = pq.top().first;
      int v = pq.top().second;
      pq.pop();
      if (d[v] == -1){
        d[v] = c;
        for (auto P : E[v]){
          int w = P.second;
          pq.push(make_pair(c + P.first, w));
        }
      }
    }
    cout << d[t] << endl;
  }
}
accuracy: 0.06382978723404255
time_ms: 20
memory_kb: 3736
final_rank: 19

code_snippet: #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;
const double eps = 0.000000001;
const double INF = 10000;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator +(point P){
    return point(x + P.x, y + P.y);
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
  point operator *(double k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
point rotate(point P, double t){
  return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t));
}
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dot(point P, point Q){
  return P.x * Q.x + P.y * Q.y;
}
double cross(point P,  point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool point_on_line(point P, line L){
  return abs(cross(vec(L), P - L.A)) < eps;
}
bool is_parallel(line L1, line L2){
  return abs(cross(vec(L1), vec(L2))) < eps;
}
bool is_same(line L1, line L2){
  return point_on_line(L2.A, L1) && point_on_line(L2.B, L1);
}
point line_intersection(line L1, line L2){
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}
bool point_on_segment(point P, line L){
  return point_on_line(P, L) && dot(P - L.A, vec(L)) > -eps && dot(P - L.B, vec(L)) < eps;
}
bool point_in_polygon(point P, vector<point> A){
  int N = A.size();
  line L1(P, point(P.x, INF));
  int cnt = 0;
  for (int i = 0; i < N; i++){
    int j = (i + 1) % N;
    line L2(A[i], A[j]);
    point X = line_intersection(L1, L2);
    if (point_on_segment(X, L1) && point_on_segment(X, L2)){
      cnt++;
    }
  }
  return cnt % 2 == 1;
}
int main(){
  int N;
  cin >> N;
  vector<int> C(N);
  vector<vector<point>> A(N);
  for (int i = 0; i < N; i++){
    cin >> C[i];
    A[i].resize(C[i]);
    for (int j = 0; j < C[i]; j++){
      cin >> A[i][j].x >> A[i][j].y;
    }
  }
  int Q;
  cin >> Q;
  vector<point> S(Q), T(Q);
  for (int i = 0; i < Q; i++){
    cin >> S[i].x >> S[i].y >> T[i].x >> T[i].y;
  }
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      A[i][j] = rotate(A[i][j], 0.01);
    }
  }
  for (int i = 0; i < Q; i++){
    S[i] = rotate(S[i], 0.01);
    T[i] = rotate(T[i], 0.01);
  }
  vector<line> L;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      int k = (j + 1) % C[i];
      L.push_back(line(A[i][j], A[i][k]));
    }
  }
  int Lcnt = L.size();
  vector<double> X;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      X.push_back(A[i][j].x);
    }
  }
  for (int i = 0; i < Q; i++){
    X.push_back(S[i].x);
    X.push_back(T[i].x);
  }
  for (int i = 0; i < Lcnt; i++){
    for (int j = i + 1; j < Lcnt; j++){
      if (!is_parallel(L[i], L[j])){
        point P = line_intersection(L[i], L[j]);
        if (point_on_segment(P, L[i]) && point_on_segment(P, L[j])){
          X.push_back(P.x);
        }
      }
    }
  }
  sort(X.begin(), X.end());
  vector<double> X2;
  X2.push_back(X[0]);
  for (int i = 1; i < X.size(); i++){
    if (X[i] - X[i - 1] > eps){
      X2.push_back(X[i]);
    }
  }
  X = X2;
  int M = X.size();
  vector<vector<double>> Y(M);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j <= r; j++){
      point P = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      Y[j].push_back(P.y);
    }
  }
  for (int i = 0; i < M; i++){
    if (!Y[i].empty()){
      int cnt = Y[i].size();
      sort(Y[i].begin(), Y[i].end());
      vector<double> Y2;
      Y2.push_back(Y[i][0]);
      for (int j = 1; j < cnt; j++){
        if (Y[i][j] - Y[i][j - 1] > eps){
          Y2.push_back(Y[i][j]);
        }
      }
      Y[i] = Y2;
    }
  }
  vector<vector<pair<int, int>>> L2(M + 1);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j < r; j++){
      point P1 = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      point P2 = line_intersection(L[i], line(point(X[j + 1], 0), point(X[j + 1], 1)));
      int p1 = lower_bound(Y[j].begin(), Y[j].end(), P1.y - eps) - Y[j].begin();
      int p2 = lower_bound(Y[j + 1].begin(), Y[j + 1].end(), P2.y - eps) - Y[j + 1].begin();
      L2[j + 1].push_back(make_pair(p1, p2));
    }
  }
  for (int i = 0; i <= M; i++){
    sort(L2[i].begin(), L2[i].end());
    L2[i].erase(unique(L2[i].begin(), L2[i].end()), L2[i].end());
  }
  vector<int> sum(M + 2);
  sum[0] = 0;
  for (int i = 0; i <= M; i++){
    sum[i + 1] = sum[i] + L2[i].size() + 1;
  }
  int V = sum[M + 1];
  vector<int> con(V, 0);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 1; j < cnt; j++){
      double px = (X[i - 1] + X[i]) / 2;
      double py = (Y[i - 1][L2[i][j - 1].first] + Y[i - 1][L2[i][j].first] + Y[i][L2[i][j - 1].second] + Y[i][L2[i][j].second]) / 4;
      point P(px, py);
      for (int k = 0; k < N; k++){
        if (point_in_polygon(P, A[k])){
          con[sum[i] + j]++;
        }
      }
    }
  }
  vector<vector<pair<int, int>>> E(V);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 0; j < cnt - 1; j++){
      int u = sum[i] + j;
      int v = sum[i] + j + 1;
      int c = abs(con[u] - con[v]);
      E[u].push_back(make_pair(c, v));
      E[v].push_back(make_pair(c, u));
    }
  }
  for (int i = 0; i < M; i++){
    int cnt = Y[i].size();
    vector<int> id1(cnt + 1), id2(cnt + 1);
    int cnt1 = L2[i].size(), cnt2 = L2[i + 1].size();
    if (cnt1 == 0){
      id1 = vector<int>(cnt + 1, sum[i]);
    } else {
      for (int j = 0; j <= L2[i][0].second; j++){
        id1[j] = sum[i];
      }
      for (int j = 0; j < cnt1 - 1; j++){
        for (int k = L2[i][j].second + 1; k < L2[i][j + 1].second + 1; k++){
          id1[k] = sum[i] + j + 1;
        }
      }
      for (int j = L2[i][cnt1 - 1].second + 1; j <= cnt; j++){
        id1[j] = sum[i] + cnt1;
      }
    }
    if (cnt2 == 0){
      id2 = vector<int>(cnt + 1, sum[i + 1]);
    } else {
      for (int j = 0; j <= L2[i + 1][0].first; j++){
        id2[j] = sum[i + 1];
      }
      for (int j = 0; j < cnt2 - 1; j++){
        for (int k = L2[i + 1][j].first + 1; k <= L2[i + 1][j + 1].first; k++){
          id2[k] = sum[i + 1] + j + 1;
        }
      }
      for (int j = L2[i + 1][cnt2 - 1].first + 1; j <= cnt; j++){
        id2[j] = sum[i + 1] + cnt2;
      }
    }
    for (int j = 0; j <= cnt; j++){
      int c = abs(con[id1[j]] - con[id2[j]]);
      E[id1[j]].push_back(make_pair(c, id2[j]));
      E[id2[j]].push_back(make_pair(c, id1[j]));
    }
  }
  for (int i = 0; i < Q; i++){
    int xs = lower_bound(X.begin(), X.end(), S[i].x - eps) - X.begin();
    int xt = lower_bound(X.begin(), X.end(), T[i].x - eps) - X.begin();
    int s;
    int cnt1 = L2[xs].size();
    if (cnt1 == 0){
      s = sum[xs];
    } else if (S[i].y < Y[xs][0]){
      s = sum[xs];
    } else if (S[i].y > Y[xs][Y[xs].size() - 1]){
      s = sum[xs] + cnt1;
    } else {
      for (int j = 0; j < cnt1 - 1; j++){
        if (Y[xs][L2[xs][j].second] < S[i].y && S[i].y < Y[xs][L2[xs][j + 1].second]){
          s = sum[xs] + j + 1;
        }
      }
    }
    int t;
    int cnt2 = L2[xt].size();
    if (cnt2 == 0){
      t = sum[xt];
    } else if (T[i].y < Y[xt][0]){
      t = sum[xt];
    } else if (T[i].y > Y[xt][Y[xt].size() - 1]){
      t = sum[xt] + cnt2;
    } else {
      for (int j = 0; j < cnt2 - 1; j++){
        if (Y[xt][L2[xt][j].second] < T[i].y && T[i].y < Y[xt][L2[xt][j + 1].second]){
          t = sum[xt] + j + 1;
        }
      }
    }
    vector<int> d(V, -1);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, s));
    while (!pq.empty()){
      int c = pq.top().first;
      int v = pq.top().second;
      pq.pop();
      if (d[v] == -1){
        d[v] = c;
        for (auto P : E[v]){
          int w = P.second;
          pq.push(make_pair(c + P.first, w));
        }
      }
    }
    cout << d[t] << endl;
  }
}
accuracy: 0.06382978723404255
time_ms: 20
memory_kb: 3784
final_rank: 20

code_snippet: #define _GLIBCXX_DEBUG
#include <bits/stdc++.h>
using namespace std;
const double eps = 0.000000001;
const double INF = 10000;
struct point{
  double x, y;
  point(){
  }
  point(double x, double y): x(x), y(y){
  }
  point operator +(point P){
    return point(x + P.x, y + P.y);
  }
  point operator -(point P){
    return point(x - P.x, y - P.y);
  }
  point operator *(double k){
    return point(x * k, y * k);
  }
  point operator /(double k){
    return point(x / k, y / k);
  }
};
point rotate(point P, double t){
  return point(P.x * cos(t) - P.y * sin(t), P.x * sin(t) + P.y * cos(t));
}
double abs(point P){
  return sqrt(P.x * P.x + P.y * P.y);
}
double dot(point P, point Q){
  return P.x * Q.x + P.y * Q.y;
}
double cross(point P,  point Q){
  return P.x * Q.y - P.y * Q.x;
}
struct line{
  point A, B;
  line(point A, point B): A(A), B(B){
  }
};
point vec(line L){
  return L.B - L.A;
}
bool point_on_line(point P, line L){
  return abs(cross(vec(L), P - L.A)) < eps;
}
bool is_parallel(line L1, line L2){
  return abs(cross(vec(L1), vec(L2))) < eps;
}
bool is_same(line L1, line L2){
  return point_on_line(L2.A, L1) && point_on_line(L2.B, L1);
}
point line_intersection(line L1, line L2){
  return L1.A + vec(L1) * cross(L2.A - L1.A, vec(L2)) / cross(vec(L1), vec(L2));
}
bool point_on_segment(point P, line L){
  return point_on_line(P, L) && dot(P - L.A, vec(L)) > 0 && dot(P - L.B, vec(L)) < 0;
}
bool point_in_polygon(point P, vector<point> A){
  int N = A.size();
  line L1(P, point(P.x, INF));
  int cnt = 0;
  for (int i = 0; i < N; i++){
    int j = (i + 1) % N;
    line L2(A[i], A[j]);
    point X = line_intersection(L1, L2);
    if (point_on_segment(X, L1) && point_on_segment(X, L2)){
      cnt++;
    }
  }
  return cnt % 2 == 1;
}
int main(){
  int N;
  cin >> N;
  vector<int> C(N);
  vector<vector<point>> A(N);
  for (int i = 0; i < N; i++){
    cin >> C[i];
    A[i].resize(C[i]);
    for (int j = 0; j < C[i]; j++){
      cin >> A[i][j].x >> A[i][j].y;
    }
  }
  int Q;
  cin >> Q;
  vector<point> S(Q), T(Q);
  for (int i = 0; i < Q; i++){
    cin >> S[i].x >> S[i].y >> T[i].x >> T[i].y;
  }
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      A[i][j] = rotate(A[i][j], 0.01);
    }
  }
  for (int i = 0; i < Q; i++){
    S[i] = rotate(S[i], 0.01);
    T[i] = rotate(T[i], 0.01);
  }
  vector<line> L;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      int k = (j + 1) % C[i];
      L.push_back(line(A[i][j], A[i][k]));
    }
  }
  int Lcnt = L.size();
  vector<double> X;
  for (int i = 0; i < N; i++){
    for (int j = 0; j < C[i]; j++){
      X.push_back(A[i][j].x);
    }
  }
  for (int i = 0; i < Q; i++){
    X.push_back(S[i].x);
    X.push_back(T[i].x);
  }
  for (int i = 0; i < Lcnt; i++){
    for (int j = i + 1; j < Lcnt; j++){
      if (!is_parallel(L[i], L[j])){
        point P = line_intersection(L[i], L[j]);
        if (point_on_segment(P, L[i]) && point_on_segment(P, L[j])){
          X.push_back(P.x);
        }
      }
    }
  }
  sort(X.begin(), X.end());
  vector<double> X2;
  X2.push_back(X[0]);
  for (int i = 1; i < X.size(); i++){
    if (X[i] - X[i - 1] > eps){
      X2.push_back(X[i]);
    }
  }
  X = X2;
  int M = X.size();
  vector<vector<double>> Y(M);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j <= r; j++){
      point P = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      Y[j].push_back(P.y);
    }
  }
  for (int i = 0; i < M; i++){
    if (!Y[i].empty()){
      int cnt = Y[i].size();
      sort(Y[i].begin(), Y[i].end());
      vector<double> Y2;
      Y2.push_back(Y[i][0]);
      for (int j = 1; j < cnt; j++){
        if (Y[i][j] - Y[i][j - 1] > eps){
          Y2.push_back(Y[i][j]);
        }
      }
      Y[i] = Y2;
    }
  }
  vector<vector<pair<int, int>>> L2(M + 1);
  for (int i = 0; i < Lcnt; i++){
    int l = lower_bound(X.begin(), X.end(), min(L[i].A.x, L[i].B.x) - eps) - X.begin();
    int r = lower_bound(X.begin(), X.end(), max(L[i].A.x, L[i].B.x) - eps) - X.begin();
    for (int j = l; j < r; j++){
      point P1 = line_intersection(L[i], line(point(X[j], 0), point(X[j], 1)));
      point P2 = line_intersection(L[i], line(point(X[j + 1], 0), point(X[j + 1], 1)));
      int p1 = lower_bound(Y[j].begin(), Y[j].end(), P1.y - eps) - Y[j].begin();
      int p2 = lower_bound(Y[j + 1].begin(), Y[j + 1].end(), P2.y - eps) - Y[j + 1].begin();
      L2[j + 1].push_back(make_pair(p1, p2));
    }
  }
  for (int i = 0; i <= M; i++){
    sort(L2[i].begin(), L2[i].end());
    L2[i].erase(unique(L2[i].begin(), L2[i].end()), L2[i].end());
  }
  vector<int> sum(M + 2);
  sum[0] = 0;
  for (int i = 0; i <= M; i++){
    sum[i + 1] = sum[i] + L2[i].size() + 1;
  }
  int V = sum[M + 1];
  vector<int> con(V, 0);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 1; j < cnt; j++){
      double px = (X[i - 1] + X[i]) / 2;
      double py = (Y[i - 1][L2[i][j - 1].first] + Y[i - 1][L2[i][j].first] + Y[i][L2[i][j - 1].second] + Y[i][L2[i][j].second]) / 4;
      point P(px, py);
      for (int k = 0; k < N; k++){
        if (point_in_polygon(P, A[k])){
          con[sum[i] + j]++;
        }
      }
    }
  }
  vector<vector<pair<int, int>>> E(V);
  for (int i = 0; i <= M; i++){
    int cnt = L2[i].size();
    for (int j = 0; j < cnt - 1; j++){
      int u = sum[i] + j;
      int v = sum[i] + j + 1;
      int c = abs(con[u] - con[v]);
      E[u].push_back(make_pair(c, v));
      E[v].push_back(make_pair(c, u));
    }
  }
  for (int i = 0; i < M; i++){
    int cnt = Y[i].size();
    vector<int> id1(cnt + 1), id2(cnt + 1);
    int cnt1 = L2[i].size(), cnt2 = L2[i + 1].size();
    if (cnt1 == 0){
      id1 = vector<int>(cnt + 1, sum[i]);
    } else {
      for (int j = 0; j <= L2[i][0].second; j++){
        id1[j] = sum[i];
      }
      for (int j = 0; j < cnt1 - 1; j++){
        for (int k = L2[i][j].second + 1; k < L2[i][j + 1].second + 1; k++){
          id1[k] = sum[i] + j + 1;
        }
      }
      for (int j = L2[i][cnt1 - 1].second + 1; j <= cnt; j++){
        id1[j] = sum[i] + cnt1;
      }
    }
    if (cnt2 == 0){
      id2 = vector<int>(cnt + 1, sum[i + 1]);
    } else {
      for (int j = 0; j <= L2[i + 1][0].first; j++){
        id2[j] = sum[i + 1];
      }
      for (int j = 0; j < cnt2 - 1; j++){
        for (int k = L2[i + 1][j].first + 1; k <= L2[i + 1][j + 1].first; k++){
          id2[k] = sum[i + 1] + j + 1;
        }
      }
      for (int j = L2[i + 1][cnt2 - 1].first + 1; j <= cnt; j++){
        id2[j] = sum[i + 1] + cnt2;
      }
    }
    for (int j = 0; j <= cnt; j++){
      int c = abs(con[id1[j]] - con[id2[j]]);
      E[id1[j]].push_back(make_pair(c, id2[j]));
      E[id2[j]].push_back(make_pair(c, id1[j]));
    }
  }
  for (int i = 0; i < Q; i++){
    int xs = lower_bound(X.begin(), X.end(), S[i].x - eps) - X.begin();
    int xt = lower_bound(X.begin(), X.end(), T[i].x - eps) - X.begin();
    int s;
    int cnt1 = L2[xs].size();
    if (cnt1 == 0){
      s = sum[xs];
    } else if (S[i].y < Y[xs][0]){
      s = sum[xs];
    } else if (S[i].y > Y[xs][Y[xs].size() - 1]){
      s = sum[xs] + cnt1;
    } else {
      for (int j = 0; j < cnt1 - 1; j++){
        if (Y[xs][L2[xs][j].second] < S[i].y && S[i].y < Y[xs][L2[xs][j + 1].second]){
          s = sum[xs] + j + 1;
        }
      }
    }
    int t;
    int cnt2 = L2[xt].size();
    if (cnt2 == 0){
      t = sum[xt];
    } else if (T[i].y < Y[xt][0]){
      t = sum[xt];
    } else if (T[i].y > Y[xt][Y[xt].size() - 1]){
      t = sum[xt] + cnt2;
    } else {
      for (int j = 0; j < cnt2 - 1; j++){
        if (Y[xt][L2[xt][j].second] < T[i].y && T[i].y < Y[xt][L2[xt][j + 1].second]){
          t = sum[xt] + j + 1;
        }
      }
    }
    vector<int> d(V, -1);
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, s));
    while (!pq.empty()){
      int c = pq.top().first;
      int v = pq.top().second;
      pq.pop();
      if (d[v] == -1){
        d[v] = c;
        for (auto P : E[v]){
          int w = P.second;
          pq.push(make_pair(c + P.first, w));
        }
      }
    }
    cout << d[t] << endl;
  }
}
accuracy: 0.06382978723404255
time_ms: 20
memory_kb: 3704
final_rank: 18

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;
 
 
#define NUM 1005
 
struct Point{
    Point(double arg_x,double arg_y){
        x = arg_x;
        y = arg_y;
    }
 
    Point(){
        x = y = 0.0;
    }
 
    Point operator + (Point p){ return Point(x+p.x,y+p.y); }
    Point operator - (Point p){ return Point(x-p.x,y-p.y);}
    Point operator * (double a){ return Point(a*x,a*y); }
    Point operator / (double a){ return Point(x/a,y/a); }
 
    double abs(){ return sqrt(norm()); }
    double norm(){ return x*x + y*y; }
 
    bool operator<(const Point &p) const{
        return x != p.x? x < p.x: y < p.y;
    }
 
    bool operator == (const Point &p) const{
        return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
    }
 
    double x,y;
};
 
typedef Point Vector;
typedef vector<Point> Polygon;
 
struct Line{
    Point p[2];
    Line(Point p1,Point p2){
        p[0] = p1;
        p[1] = p2;
    }
    Line(){
 
    }
};
 
 
vector<Polygon> POLYGON,MAP;
vector<Line> LINE;
int table[NUM],min_dist[NUM][NUM];
bool is_adj[NUM][NUM];
 
 
double norm(Vector a){
    return a.x*a.x+a.y*a.y;
}
 
double abs(Vector a){
    return sqrt(norm(a));
}
 
double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}
 
double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}
 
static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -1;
static const int ON_SEGMENT = 0;
 
int ccw(Point p0,Point p1,Point p2){
 
    Vector a = p1 - p0;
    Vector b = p2 - p0;
 
    if(cross(a,b) > EPS)return COUNTER_CLOCKWISE;
    if(cross(a,b) < -EPS)return CLOCKWISE;
    if(dot(a,b) < -EPS)return ONLINE_BACK;
    if(a.norm() < b.norm())return ONLINE_FRONT;
 
    return ON_SEGMENT;
}
 
/*
 * IN 2
 * ON 1
 * OUT 0
 *
 */
int contains(Polygon g,Point p){
    int n = g.size();
    bool x = false;
    for(int i = 0; i < n; i++){
        Point a = g[i]-p,b = g[(i+1)%n]-p;
        if(abs(cross(a,b)) < EPS && dot(a,b) < EPS)return 1;
        if(a.y > b.y)swap(a,b);
        if(a.y < EPS && EPS < b.y && cross(a,b) > EPS) x = !x;
    }
    return (x ? 2:0);
}
 
//交点を求める関数
Point calc_Cross_Point(double x1,double x2,double x3,double x4,double y1,double y2,double y3,double y4){
    Point ret;
    ret.x = ((x2-x1)*(y3*(x4-x3)+x3*(y3-y4))-(x4-x3)*(y1*(x2-x1)+x1*(y1-y2)))/((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1));
    if(x1 != x2){
        ret.y = ((y2-y1)*ret.x+y1*(x2-x1)+x1*(y1-y2))/(x2-x1);
    }else{
        ret.y = ((y4-y3)*ret.x+y3*(x4-x3)+x3*(y3-y4))/(x4-x3);
    }
    return ret;
}
 
//インタフェース関数
Point calc_Cross_Point(Point a,Point b,Point c,Point d){
    return calc_Cross_Point(a.x,b.x,c.x,d.x,a.y,b.y,c.y,d.y);
}
 
Point calc_Cross_Point(Line A,Line B){
 
    return calc_Cross_Point(A.p[0],A.p[1],B.p[0],B.p[1]);
}
 
Polygon ConvexCut(Polygon g,Point a,Point b){
 
    Polygon ret;
    int N = g.size();
 
    for(int i = 0; i < g.size(); i++){
        Point A = g[i], B = g[(i+1)%N];
        if(ccw(a,b,A) != -1)ret.push_back(A);
        if(ccw(a,b,A)*ccw(a,b,B) == -1)ret.push_back(calc_Cross_Point(a,b,A,B));
    }
 
    return ret;
}
 
 
int main(){
 
    int N;
 
    scanf("%d",&N);
 
    int tmp;
 
    for(int loop = 0; loop < N; loop++){
 
        scanf("%d",&tmp);
 
        Polygon polygon;
 
        for(int i = 0; i < tmp; i++){
 
            double X,Y;
            scanf("%lf %lf",&X,&Y);
 
            polygon.push_back(Point(X,Y));
        }
        POLYGON.push_back(polygon);
    }
 
    //線分を先に作っておく
    for(int i = 0; i < N; i++){
        for(int k = 0; k < POLYGON[i].size(); k++){
 
            LINE.push_back(Line(POLYGON[i][k],POLYGON[i][(k+1)%POLYGON[i].size()]));
        }
    }
 
    //十分に大きい四角形でマップを囲む
    Polygon big_square;
 
    big_square.push_back(Point(NUM,NUM));
    big_square.push_back(Point(NUM,-NUM));
    big_square.push_back(Point(-NUM,-NUM));
    big_square.push_back(Point(-NUM,NUM));
 
    MAP.push_back(big_square);
 
    //領域の分割処理
    for(int i = 0; i < LINE.size(); i++){
 
        vector<Polygon> NEXT;
 
        for(int k = 0; k < MAP.size(); k++){
 
            Polygon polygon = ConvexCut(MAP[k],LINE[i].p[0],LINE[i].p[1]);
            if(polygon.size() >= 3){
 
                NEXT.push_back(polygon);
            }
            polygon = ConvexCut(MAP[k],LINE[i].p[1],LINE[i].p[0]);
            if(polygon.size() >= 3){
 
                NEXT.push_back(polygon);
            }
        }
        MAP = NEXT;
    }
 
    //領域の隣接関係を調べる
    for(int i = 0; i < MAP.size(); i++){
        for(int k = 0; k < MAP.size(); k++){
            if(k == i){
                is_adj[i][k] = true;
                continue;
            }
 
            bool FLG = false;
 
            //同じ辺を共有しているか調べる
            for(int a = 0; a < MAP[i].size(); a++){
                for(int b = 0; b < MAP[k].size(); b++){
 
                    Line A = Line(MAP[i][a],MAP[i][(a+1)%MAP[i].size()]);
                    Line B = Line(MAP[k][b],MAP[k][(b+1)%MAP[k].size()]);
 
                    if((abs(A.p[0]-B.p[0]) < EPS && abs(A.p[1]-B.p[1]) < EPS) || (abs(A.p[0]-B.p[1]) < EPS && abs(A.p[1]-B.p[0]) < EPS)){
 
                        FLG = true;
                        break;
                    }
                }
                if(FLG)break;
            }
 
            is_adj[i][k] = FLG;
        }
    }
 
    for(int i = 0; i < MAP.size(); i++){
 
        Vector vec;
        for(int k = 0; k < MAP[i].size(); k++){
 
            vec = vec+MAP[i][k];
        }
        vec = vec/MAP[i].size();
 
        int count = 0;
 
        for(int k = 0; k < POLYGON.size(); k++){
 
            if(contains(POLYGON[k],vec) == 2){
 
                count++;
            }
        }
        table[i] = count;
    }
 
 
 
    for(int i = 0; i < MAP.size(); i++){
        for(int k = 0; k < MAP.size(); k++){
            if(k != i){
 
                if(is_adj[i][k]){
 
                    min_dist[i][k] = abs(table[i]-table[k]);
 
                }else{
                    min_dist[i][k] = BIG_NUM;
                }
            }else{
 
                min_dist[i][k] = 0;
            }
        }
    }
 
    for(int mid = 0; mid < MAP.size(); mid++){
        for(int st = 0; st < MAP.size(); st++){
            if(min_dist[st][mid] == BIG_NUM)continue;
            for(int gl = 0; gl < MAP.size(); gl++){
                if(min_dist[mid][gl] == BIG_NUM)continue;
 
                min_dist[st][gl] = min(min_dist[st][gl],min_dist[st][mid]+min_dist[mid][gl]);
            }
        }
    }
 
    int num_query;
    scanf("%d",&num_query);
 
    int start = -1,goal = -1;
    Point enemy,building;
 
    for(int loop = 0; loop < num_query; loop++){
 
        scanf("%lf %lf %lf %lf",&enemy.x,&enemy.y,&building.x,&building.y);
 
        for(int i = 0; i < MAP.size(); i++){
 
            if(contains(MAP[i],enemy) >= 1){
                start = i;
                break;
            }
        }
        for(int i = 0; i < MAP.size(); i++){
 
            if(contains(MAP[i],building) >= 1){
 
                goal = i;
                break;
            }
        }
        printf("%d\n",min_dist[start][goal]);
    }
 
    return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4440
final_rank: 15

code_snippet: #include<bits/stdc++.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define HUGE_NUM 99999999999999999
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 1005

struct Point{
	Point(double arg_x,double arg_y){
		x = arg_x;
		y = arg_y;
	}

	Point(){
		x = y = 0.0;
	}

	Point operator + (Point p){ return Point(x+p.x,y+p.y); }
	Point operator - (Point p){ return Point(x-p.x,y-p.y);}
	Point operator * (double a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

	bool operator == (const Point &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}

	double x,y;
};

typedef Point Vector;
typedef vector<Point> Polygon;

struct Line{
	Point p[2];
	Line(Point p1,Point p2){
		p[0] = p1;
		p[1] = p2;
	}
	Line(){

	}
};


vector<Polygon> POLYGON,MAP;
vector<Line> LINE;
int table[NUM],min_dist[NUM][NUM];
bool is_adj[NUM][NUM];


double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
    return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
    return a.x*b.x + a.y*b.y;
}

static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -1;
static const int ON_SEGMENT = 0;

int ccw(Point p0,Point p1,Point p2){

	Vector a = p1 - p0;
	Vector b = p2 - p0;

	if(cross(a,b) > EPS)return COUNTER_CLOCKWISE;
	if(cross(a,b) < -EPS)return CLOCKWISE;
	if(dot(a,b) < -EPS)return ONLINE_BACK;
	if(a.norm() < b.norm())return ONLINE_FRONT;

	return ON_SEGMENT;
}

/*
 * IN 2
 * ON 1
 * OUT 0
 *
 */
int contains(Polygon g,Point p){
	int n = g.size();
	bool x = false;
	for(int i = 0; i < n; i++){
		Point a = g[i]-p,b = g[(i+1)%n]-p;
		if(abs(cross(a,b)) < EPS && dot(a,b) < EPS)return 1;
		if(a.y > b.y)swap(a,b);
		if(a.y < EPS && EPS < b.y && cross(a,b) > EPS) x = !x;
	}
	return (x ? 2:0);
}

//交点を求める関数
Point calc_Cross_Point(double x1,double x2,double x3,double x4,double y1,double y2,double y3,double y4){
	Point ret;
	ret.x = ((x2-x1)*(y3*(x4-x3)+x3*(y3-y4))-(x4-x3)*(y1*(x2-x1)+x1*(y1-y2)))/((y2-y1)*(x4-x3)-(y4-y3)*(x2-x1));
	if(x1 != x2){
		ret.y = ((y2-y1)*ret.x+y1*(x2-x1)+x1*(y1-y2))/(x2-x1);
	}else{
		ret.y = ((y4-y3)*ret.x+y3*(x4-x3)+x3*(y3-y4))/(x4-x3);
	}
	return ret;
}

//インタフェース関数
Point calc_Cross_Point(Point a,Point b,Point c,Point d){
	return calc_Cross_Point(a.x,b.x,c.x,d.x,a.y,b.y,c.y,d.y);
}

Point calc_Cross_Point(Line A,Line B){

	return calc_Cross_Point(A.p[0],A.p[1],B.p[0],B.p[1]);
}

Polygon ConvexCut(Polygon g,Point a,Point b){

	Polygon ret;
	int N = g.size();

	for(int i = 0; i < g.size(); i++){
		Point A = g[i], B = g[(i+1)%N];
		if(ccw(a,b,A) != -1)ret.push_back(A);
		if(ccw(a,b,A)*ccw(a,b,B) == -1)ret.push_back(calc_Cross_Point(a,b,A,B));
	}

	return ret;
}


int main(){

	int N;

	scanf("%d",&N);

	int tmp;

	for(int loop = 0; loop < N; loop++){

		scanf("%d",&tmp);

		Polygon polygon;

		for(int i = 0; i < tmp; i++){

			double X,Y;
			scanf("%lf %lf",&X,&Y);

			polygon.push_back(Point(X,Y));
		}
		POLYGON.push_back(polygon);
	}

	//線分を先に作っておく
	for(int i = 0; i < N; i++){
		for(int k = 0; k < POLYGON[i].size(); k++){

			LINE.push_back(Line(POLYGON[i][k],POLYGON[i][(k+1)%POLYGON[i].size()]));
		}
	}

	//十分に大きい四角形でマップを囲む
	Polygon big_square;

	big_square.push_back(Point(NUM,NUM));
	big_square.push_back(Point(NUM,-NUM));
	big_square.push_back(Point(-NUM,-NUM));
	big_square.push_back(Point(-NUM,NUM));

	MAP.push_back(big_square);

	//領域の分割処理
	for(int i = 0; i < LINE.size(); i++){

		vector<Polygon> NEXT;

		for(int k = 0; k < MAP.size(); k++){

			Polygon polygon = ConvexCut(MAP[k],LINE[i].p[0],LINE[i].p[1]);
			if(polygon.size() >= 3){

				NEXT.push_back(polygon);
			}
			polygon = ConvexCut(MAP[k],LINE[i].p[1],LINE[i].p[0]);
			if(polygon.size() >= 3){

				NEXT.push_back(polygon);
			}
		}
		MAP = NEXT;
	}

	//領域の隣接関係を調べる
	for(int i = 0; i < MAP.size(); i++){
		for(int k = 0; k < MAP.size(); k++){
			if(k == i){
				is_adj[i][k] = true;
				continue;
			}

			bool FLG = false;

			//同じ辺を共有しているか調べる
			for(int a = 0; a < MAP[i].size(); a++){
				for(int b = 0; b < MAP[k].size(); b++){

					Line A = Line(MAP[i][a],MAP[i][(a+1)%MAP[i].size()]);
					Line B = Line(MAP[k][b],MAP[k][(b+1)%MAP[k].size()]);

					if((abs(A.p[0]-B.p[0]) < EPS && abs(A.p[1]-B.p[1]) < EPS) || (abs(A.p[0]-B.p[1]) < EPS && abs(A.p[1]-B.p[0]) < EPS)){

						FLG = true;
						break;
					}
				}
				if(FLG)break;
			}

			is_adj[i][k] = FLG;
		}
	}

	for(int i = 0; i < MAP.size(); i++){

		Vector vec;
		for(int k = 0; k < MAP[i].size(); k++){

			vec = vec+MAP[i][k];
		}
		vec = vec/MAP[i].size();

		int count = 0;

		for(int k = 0; k < POLYGON.size(); k++){

			if(contains(POLYGON[k],vec) == 2){

				count++;
			}
		}
		table[i] = count;
	}



	for(int i = 0; i < MAP.size(); i++){
		for(int k = 0; k < MAP.size(); k++){
			if(k != i){

				if(is_adj[i][k]){

					min_dist[i][k] = abs(table[i]-table[k]);

				}else{
					min_dist[i][k] = BIG_NUM;
				}
			}else{

				min_dist[i][k] = 0;
			}
		}
	}

	for(int mid = 0; mid < MAP.size(); mid++){
		for(int st = 0; st < MAP.size(); st++){
			if(min_dist[st][mid] == BIG_NUM)continue;
			for(int gl = 0; gl < MAP.size(); gl++){
				if(min_dist[mid][gl] == BIG_NUM)continue;

				min_dist[st][gl] = min(min_dist[st][gl],min_dist[st][mid]+min_dist[mid][gl]);
			}
		}
	}

	int num_query;
	scanf("%d",&num_query);

	int start = -1,goal = -1;
	Point enemy,building;

	for(int loop = 0; loop < num_query; loop++){

		scanf("%lf %lf %lf %lf",&enemy.x,&enemy.y,&building.x,&building.y);

		for(int i = 0; i < MAP.size(); i++){

			if(contains(MAP[i],enemy) >= 1){
				start = i;
				break;
			}
		}
		for(int i = 0; i < MAP.size(); i++){

			if(contains(MAP[i],building) >= 1){

				goal = i;
				break;
			}
		}
		printf("%d\n",min_dist[start][goal]);
	}

	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 4404
final_rank: 11

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i < (n); i++)
typedef double D;
typedef complex<D> P;
typedef pair<P, P> L;
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7; 
#define X real()
#define Y imag()
#define LE(n,m) ((n) < (m) + EPS)
#define GE(n,m) ((n) + EPS > (m))
#define EQ(n,m) (abs((n)-(m)) < EPS)
D dot(P a, P b) {return (conj(a)*b).X; }
D cross(P a, P b) {  return (conj(a)*b).Y;}
int ccw(P a, P b, P c) {
  b -= a;  c -= a;
  if (cross(b,c) >  EPS) return +1;
  if (cross(b,c) < -EPS) return -1; 
  if (dot(b,c)   < -EPS) return +2; 
  if (norm(b) < norm(c)) return -2; 
  return 0; 
}
bool isparallel(L a,L b){
  return cross(a.first-a.second,b.first-b.second) == 0.0 ;
}

bool isecLP(P a1, P a2, P b) {
  return abs(ccw(a1, a2, b)) != 1;  
}

bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2-a1, b2-b1, 0) || isecLP(a1, b1, b2);
}

bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2-a1, b1-a1) * cross(a2-a1, b2-a1) < EPS;
}

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;
}
 
// 線分と点
bool isecSP(P a1, P a2, P b) {
  return !ccw(a1, a2, b);
  //return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;  //Perfective
}
 

P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2-b1, b1-a1);
  D d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  if (EQ(d2, 0)) throw "kouten ga nai";   // 交点がない
  return a1 + d1/d2 * (a2-a1);
}
namespace std {
  bool operator<(const P a, const P b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}

VP convexHull(VP ps) {  
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++])
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;  
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--])
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}

bool isCcwConvex(const VP& ps) {
  int n = ps.size();
  rep (i, n) if (ccw(ps[i], ps[(i+1) % n], ps[(i+2) % n]) == -1) return false;
  return true;
}

int inConvex(P p, const VP& ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep (i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc) return 2;  // 線分上に存在
    if (ccwc != dir) return 0;
  }
  return 1;
}

int inPolygon(const VP& ps, P p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}
 

VP convexCut(const VP& ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i,n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1) ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1) ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP&);
bool isec_CS(const VP& ps, P a1, P a2){
  rep(i,ps.size()){
    if(isecLS(ps[i],ps[(i+1)%ps.size()],a1,a2))return true;
  }
  return false;
}

void divide_convex(VP ps,const VP &s1,const VP &s2,Vcon &V){
  bool flag = false;
  for(int i=0;i<s1.size();i++){
    VP convex_1=convexCut(ps,s1[i],s2[i]);
    VP convex_2=convexCut(ps,s2[i],s1[i]);
    if(area(convex_1)<EPS||area(convex_2)<EPS)continue;
    divide_convex(convex_1,s1,s2,V);
    divide_convex(convex_2,s1,s2,V);
    flag = true;
    break;
  }
  if(!flag) V.push_back(ps);
  return;
}


// 全く同じ辺を共有してれば１を返す。
bool isecCC(VP a1,VP a2){
  rep(i,a1.size()){
    rep(j,a2.size()){
      P e1=a1[i];
      P e2=a1[(i+1)%a1.size()];
      P e3=a2[j];
      P e4=a2[(j+1)%a2.size()];
      if(abs(e1-e3)<EPS&&abs(e2-e4)<EPS)return 1;
      if(abs(e1-e4)<EPS&&abs(e2-e3)<EPS)return 1;
    }
  }
  return 0;
}

// 頂点抜き
vector<vector<int> > Convex_arrangement(const Vcon& ps){
  int n=ps.size();
  vector<vector<int> >V(n,vector<int>(n));
  rep(i,n)rep(j,n)V[i][j]=1e8;
  rep(i,n){
    for(int j=i+1;j<n;j++){
      if(isecCC(ps[i],ps[j])){
        V[i][j]=1;
        V[j][i]=1;
      }
    }
  }
  return V;
}
 
// 多角形の符号付面積
D area(const VP& ps) {
  D a = 0;
  rep (i, ps.size()) a += cross(ps[i], ps[(i+1) % ps.size()]);
  return a / 2;
}

// 多角形a1が凸多角形a2を内包してるか
bool con_in_con(VP a1,VP a2){
  int p=0;
  rep(j,a2.size()){
    if(inPolygon(a1,a2[j])==0)return 0;
    if(inPolygon(a1,(a2[j]+a2[(j+1)%a2.size()])/2.0)==0)return 0;
  }
  return 1;
}

signed main(){
  vector<P>s1,s2;
  int n,m,q;
  cin>>n;
  VP V[n];
  rep(i,n){
    cin>>m;
    D x[m],y[m];
    rep(j,m){
      cin>>x[j]>>y[j];
      V[i].push_back(P(x[j],y[j]));
    }
    rep(j,m){
      s1.push_back(P(x[j],y[j]));
      s2.push_back(P(x[(j+1)%m],y[(j+1)%m]));
    }
  }
 
  VP convex;
  convex.push_back(P(-200000,-200000));
  convex.push_back(P(200000,-200000));
  convex.push_back(P(200000,200000));
  convex.push_back(P(-200000,200000));
  Vcon v;
 
  divide_convex(convex,s1,s2,v);
  vector<vector<int> > a=Convex_arrangement(v);
 
  int cnt[v.size()]={};
 
  rep(i,v.size()){
    rep(j,n){
      if(con_in_con(V[j],v[i])){
        cnt[i]++;
      }
    }
  }
  rep(i,v.size()){
    rep(j,v.size()){
      if(i==j)a[i][j]=0;
      if(a[i][j]==1){
        a[i][j]=abs(cnt[i]-cnt[j]);
      }
    }
  }
 
  rep(k,v.size())
    rep(i,v.size())
      rep(j,v.size())
        a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
 
  cin>>q;
  while(q--){
    int A=1e9,B=1e9;
    D a1,a2,b1,b2;
    cin>>a1>>a2>>b1>>b2;
    rep(i,v.size()){
      if(inPolygon(v[i],P(a1,a2))){
        A=i;
        if(inPolygon(v[i],P(a1,a2))==1)break;
      }
    }
    rep(i,v.size()){
      if(inPolygon(v[i],P(b1,b2))){
        B=i;
        if(inPolygon(v[i],P(b1,b2))==1)break;
      }
    }
    cout<<a[A][B]<<endl;
  }
}
accuracy: 1
time_ms: 60
memory_kb: 3528
final_rank: 10

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i < (n); i++)
typedef double D;      // 座標値の型。doubleかlong doubleを想定
typedef complex<D> P;  // Point
typedef pair<P, P> L;  // Line
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7;    // 許容誤差。問題によって変える
#define X real()
#define Y imag()
#define LE(n,m) ((n) < (m) + EPS)
#define GE(n,m) ((n) + EPS > (m))
#define EQ(n,m) (abs((n)-(m)) < EPS)
 
// 内積　dot(a,b) = |a||b|cosθ
D dot(P a, P b) {return (conj(a)*b).X; }
 
// 外積　cross(a,b) = |a||b|sinθ
D cross(P a, P b) {  return (conj(a)*b).Y;}
 
// 点の進行方向
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 isparallel(L a,L b){
  return cross(a.first-a.second,b.first-b.second) == 0.0 ;
}
 
/* 交差判定　直線・線分は縮退してはならない。接する場合は交差するとみなす。isecはintersectの略 */
 
// 直線と点
bool isecLP(P a1, P a2, P b) {
  return abs(ccw(a1, a2, b)) != 1;  // return EQ(cross(a2-a1, b-a1), 0); と等価
}
 
// 直線と直線
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2-a1, b2-b1, 0) || isecLP(a1, b1, b2);
}
 
// 直線と線分
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2-a1, b1-a1) * cross(a2-a1, b2-a1) < EPS;
}
 
// 線分と線分
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;
}
 
// 線分と点
bool isecSP(P a1, P a2, P b) {
  //return !ccw(a1, a2, b);
   return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;  //Perfective
}
 

P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2-b1, b1-a1);
  D d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  if (EQ(d2, 0)) throw "kouten ga nai";   // 交点がない
  return a1 + d1/d2 * (a2-a1);
}
namespace std {
  bool operator<(const P a, const P b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}
 
// 凸包
VP convexHull(VP ps) {  // 元の点集合がソートされていいならVP&に
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;  // 余計な点も含むなら == -1 とする
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}
 
// 凸判定。縮退を認めないならccwの判定部分を != 1 とする
bool isCcwConvex(const VP& ps) {
  int n = ps.size();
  rep (i, n) if (ccw(ps[i], ps[(i+1) % n], ps[(i+2) % n]) == -1) return false;
  return true;
}
 
// 凸多角形の内部判定　O(n)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inConvex(P p, const VP& ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep (i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc) return 2;  // 線分上に存在
    if (ccwc != dir) return 0;
  }
  return 1;
}
// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(const VP& ps, P p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}
 
// 凸多角形クリッピング
VP convexCut(const VP& ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i,n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1) ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1) ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP&);
bool isec_CS(const VP& ps, P a1, P a2){
  rep(i,ps.size()){
    if(isecLS(ps[i],ps[(i+1)%ps.size()],a1,a2))return true;
  }
  return false;
}

void divide_convex(VP ps,const VP &s1,const VP &s2,Vcon &V){
  bool flag = false;
  for(int i=0;i<s1.size();i++){
    VP convex_1=convexCut(ps,s1[i],s2[i]);
    VP convex_2=convexCut(ps,s2[i],s1[i]);
    if(area(convex_1)<EPS||area(convex_2)<EPS)continue;
    divide_convex(convex_1,s1,s2,V);
    divide_convex(convex_2,s1,s2,V);
    flag = true;
    break;
  }
  if(!flag) V.push_back(ps);
  return;
}


// 全く同じ辺を共有してれば１を返す。
bool isecCC(VP a1,VP a2){
  rep(i,a1.size()){
    rep(j,a2.size()){
      P e1=a1[i];
      P e2=a1[(i+1)%a1.size()];
      P e3=a2[j];
      P e4=a2[(j+1)%a2.size()];
      if(abs(e1-e3)<EPS&&abs(e2-e4)<EPS)return 1;
      if(abs(e1-e4)<EPS&&abs(e2-e3)<EPS)return 1;
    }
  }
  return 0;
}

// 頂点抜き
vector<vector<int> > Convex_arrangement(const Vcon& ps){
  int n=ps.size();
  vector<vector<int> >V(n,vector<int>(n));
  rep(i,n)rep(j,n)V[i][j]=1e8;
  rep(i,n){
    for(int j=i+1;j<n;j++){
      if(isecCC(ps[i],ps[j])){
        V[i][j]=1;
        V[j][i]=1;
      }
    }
  }
  return V;
}
 
// 多角形の符号付面積
D area(const VP& ps) {
  D a = 0;
  rep (i, ps.size()) a += cross(ps[i], ps[(i+1) % ps.size()]);
  return a / 2;
}

bool con_in_con(VP a1,VP a2){
  int p=0;
  rep(j,a2.size()){
    int sum=0,f=0;
    if(inPolygon(a1,a2[j])==0)return 0;
    if(inPolygon(a1,(a2[j]+a2[(j+1)%a2.size()])/2.0)==0)return 0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],a2[j]))goto L1;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],a2[j],P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    L1:;
    sum=f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0))goto L;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0,P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    sum=0;
    L:;
  }
  return 1;
}

signed main(){
  vector<P>s1,s2;
  int n,m,q;
  cin>>n;
  VP V[n];
  rep(i,n){
    cin>>m;
    D x[m],y[m];
    rep(j,m){
      cin>>x[j]>>y[j];
      V[i].push_back(P(x[j],y[j]));
    }
    rep(j,m){
      s1.push_back(P(x[j],y[j]));
      s2.push_back(P(x[(j+1)%m],y[(j+1)%m]));
    }
  }
 
  VP convex;
  convex.push_back(P(-200000,-200000));
  convex.push_back(P(200000,-200000));
  convex.push_back(P(200000,200000));
  convex.push_back(P(-200000,200000));
  Vcon v;
 
  divide_convex(convex,s1,s2,v);
  vector<vector<int> > a=Convex_arrangement(v);
 
  int cnt[v.size()]={};
 
  rep(i,v.size()){
    rep(j,n){
      if(con_in_con(V[j],v[i])){
        cnt[i]++;
      }
    }
  }
  rep(i,v.size()){
    rep(j,v.size()){
      if(i==j)a[i][j]=0;
      if(a[i][j]==1){
        a[i][j]=abs(cnt[i]-cnt[j]);
      }
    }
  }
 
  rep(k,v.size())
    rep(i,v.size())
      rep(j,v.size())
        a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
 
  cin>>q;
  while(q--){
    int A=1e9,B=1e9;
    D a1,a2,b1,b2;
    cin>>a1>>a2>>b1>>b2;
    rep(i,v.size()){
      if(inPolygon(v[i],P(a1,a2))){
        A=i;
        if(inPolygon(v[i],P(a1,a2))==1)break;
      }
    }
    rep(i,v.size()){
      if(inPolygon(v[i],P(b1,b2))){
        B=i;
        if(inPolygon(v[i],P(b1,b2))==1)break;
      }
    }
    cout<<a[A][B]<<endl;
  }
}
accuracy: 1
time_ms: 60
memory_kb: 3568
final_rank: 13

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i < (n); i++)
typedef double D;      // 座標値の型。doubleかlong doubleを想定
typedef complex<D> P;  // Point
typedef pair<P, P> L;  // Line
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7;    // 許容誤差。問題によって変える
#define X real()
#define Y imag()
#define LE(n,m) ((n) < (m) + EPS)
#define GE(n,m) ((n) + EPS > (m))
#define EQ(n,m) (abs((n)-(m)) < EPS)
 
// 内積　dot(a,b) = |a||b|cosθ
D dot(P a, P b) {return (conj(a)*b).X; }
 
// 外積　cross(a,b) = |a||b|sinθ
D cross(P a, P b) {  return (conj(a)*b).Y;}
 
// 点の進行方向
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 isparallel(L a,L b){
  return cross(a.first-a.second,b.first-b.second) == 0.0 ;
}
 
/* 交差判定　直線・線分は縮退してはならない。接する場合は交差するとみなす。isecはintersectの略 */
 
// 直線と点
bool isecLP(P a1, P a2, P b) {
  return abs(ccw(a1, a2, b)) != 1;  // return EQ(cross(a2-a1, b-a1), 0); と等価
}
 
// 直線と直線
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2-a1, b2-b1, 0) || isecLP(a1, b1, b2);
}
 
// 直線と線分
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2-a1, b1-a1) * cross(a2-a1, b2-a1) < EPS;
}
 
// 線分と線分
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;
}
 
// 線分と点
bool isecSP(P a1, P a2, P b) {
  //return !ccw(a1, a2, b);
   return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;  //Perfective
}
 

P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2-b1, b1-a1);
  D d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  if (EQ(d2, 0)) throw "kouten ga nai";   // 交点がない
  return a1 + d1/d2 * (a2-a1);
}
namespace std {
  bool operator<(const P a, const P b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}
 
// 凸包
VP convexHull(VP ps) {  // 元の点集合がソートされていいならVP&に
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;  // 余計な点も含むなら == -1 とする
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}
 
// 凸判定。縮退を認めないならccwの判定部分を != 1 とする
bool isCcwConvex(const VP& ps) {
  int n = ps.size();
  rep (i, n) if (ccw(ps[i], ps[(i+1) % n], ps[(i+2) % n]) == -1) return false;
  return true;
}
 
// 凸多角形の内部判定　O(n)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inConvex(P p, const VP& ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep (i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc) return 2;  // 線分上に存在
    if (ccwc != dir) return 0;
  }
  return 1;
}
// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(const VP& ps, P p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}
 
// 凸多角形クリッピング
VP convexCut(const VP& ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i,n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1) ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1) ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP&);
bool isec_CS(const VP& ps, P a1, P a2){
  rep(i,ps.size()){
    if(isecLS(ps[i],ps[(i+1)%ps.size()],a1,a2))return true;
  }
  return false;
}

void divide_convex(VP ps,const VP &s1,const VP &s2,Vcon &V){
  bool flag = false;
  for(int i=0;i<s1.size();i++){
    VP convex_1=convexCut(ps,s1[i],s2[i]);
    VP convex_2=convexCut(ps,s2[i],s1[i]);
    if(area(convex_1)<EPS||area(convex_2)<EPS)continue;
    divide_convex(convex_1,s1,s2,V);
    divide_convex(convex_2,s1,s2,V);
    flag = true;
    break;
  }
  if(!flag) V.push_back(ps);
  return;
}


// 全く同じ辺を共有してれば１を返す。
bool isecCC(VP a1,VP a2){
  rep(i,a1.size()){
    rep(j,a2.size()){
      P e1=a1[i];
      P e2=a1[(i+1)%a1.size()];
      P e3=a2[j];
      P e4=a2[(j+1)%a2.size()];
      if(abs(e1-e3)<EPS&&abs(e2-e4)<EPS)return 1;
      if(abs(e1-e4)<EPS&&abs(e2-e3)<EPS)return 1;
    }
  }
  return 0;
}

// 頂点抜き
vector<vector<int> > Convex_arrangement(const Vcon& ps){
  int n=ps.size();
  vector<vector<int> >V(n,vector<int>(n));
  rep(i,n)rep(j,n)V[i][j]=1e8;
  rep(i,n){
    for(int j=i+1;j<n;j++){
      if(isecCC(ps[i],ps[j])){
        V[i][j]=1;
        V[j][i]=1;
      }
    }
  }
  return V;
}
 
// 多角形の符号付面積
D area(const VP& ps) {
  D a = 0;
  rep (i, ps.size()) a += cross(ps[i], ps[(i+1) % ps.size()]);
  return a / 2;
}

bool con_in_con(VP a1,VP a2){
  int p=0;
  rep(j,a2.size()){
    int sum=0,f=0;
    if(inPolygon(a1,a2[j])==0)return 0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],a2[j]))goto L1;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],a2[j],P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    L1:;
    sum=f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0))goto L;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0,P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    sum=0;
    L:;
  }
  return !p;
}

signed main(){
  vector<P>s1,s2;
  int n,m,q;
  cin>>n;
  VP V[n];
  rep(i,n){
    cin>>m;
    D x[m],y[m];
    rep(j,m){
      cin>>x[j]>>y[j];
      V[i].push_back(P(x[j],y[j]));
    }
    rep(j,m){
      s1.push_back(P(x[j],y[j]));
      s2.push_back(P(x[(j+1)%m],y[(j+1)%m]));
    }
  }
 
  VP convex;
  convex.push_back(P(-200000,-200000));
  convex.push_back(P(200000,-200000));
  convex.push_back(P(200000,200000));
  convex.push_back(P(-200000,200000));
  Vcon v;
 
  divide_convex(convex,s1,s2,v);
  vector<vector<int> > a=Convex_arrangement(v);
 
  int cnt[v.size()]={};
 
  rep(i,v.size()){
    rep(j,n){
      if(con_in_con(V[j],v[i])){
        cnt[i]++;
      }
    }
  }
  rep(i,v.size()){
    rep(j,v.size()){
      if(i==j)a[i][j]=0;
      if(a[i][j]==1){
        a[i][j]=abs(cnt[i]-cnt[j]);
      }
    }
  }
 
  rep(k,v.size())
    rep(i,v.size())
      rep(j,v.size())
        a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
 
  cin>>q;
  while(q--){
    int A=1e9,B=1e9;
    D a1,a2,b1,b2;
    cin>>a1>>a2>>b1>>b2;
    rep(i,v.size()){
      if(inPolygon(v[i],P(a1,a2))){
        A=i;
        if(inPolygon(v[i],P(a1,a2))==1)break;
      }
    }
    rep(i,v.size()){
      if(inPolygon(v[i],P(b1,b2))){
        B=i;
        if(inPolygon(v[i],P(b1,b2))==1)break;
      }
    }
    cout<<a[A][B]<<endl;
  }
}
accuracy: 1
time_ms: 60
memory_kb: 3484
final_rank: 8

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i < (n); i++)
typedef double D;      // 座標値の型。doubleかlong doubleを想定
typedef complex<D> P;  // Point
typedef pair<P, P> L;  // Line
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-10;    // 許容誤差。問題によって変える
#define X real()
#define Y imag()
#define LE(n,m) ((n) < (m) + EPS)
#define GE(n,m) ((n) + EPS > (m))
#define EQ(n,m) (abs((n)-(m)) < EPS)
 
// 内積　dot(a,b) = |a||b|cosθ
D dot(P a, P b) {return (conj(a)*b).X; }
 
// 外積　cross(a,b) = |a||b|sinθ
D cross(P a, P b) {  return (conj(a)*b).Y;}
 
// 点の進行方向
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 isparallel(L a,L b){
  return cross(a.first-a.second,b.first-b.second) == 0.0 ;
}
 
/* 交差判定　直線・線分は縮退してはならない。接する場合は交差するとみなす。isecはintersectの略 */
 
// 直線と点
bool isecLP(P a1, P a2, P b) {
  return abs(ccw(a1, a2, b)) != 1;  // return EQ(cross(a2-a1, b-a1), 0); と等価
}
 
// 直線と直線
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2-a1, b2-b1, 0) || isecLP(a1, b1, b2);
}
 
// 直線と線分
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2-a1, b1-a1) * cross(a2-a1, b2-a1) < EPS;
}
 
// 線分と線分
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;
}
 
// 線分と点
bool isecSP(P a1, P a2, P b) {
  //return !ccw(a1, a2, b);
   return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;  //Perfective
}
 

P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2-b1, b1-a1);
  D d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  if (EQ(d2, 0)) throw "kouten ga nai";   // 交点がない
  return a1 + d1/d2 * (a2-a1);
}
namespace std {
  bool operator<(const P a, const P b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}
 
// 凸包
VP convexHull(VP ps) {  // 元の点集合がソートされていいならVP&に
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;  // 余計な点も含むなら == -1 とする
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}
 
// 凸判定。縮退を認めないならccwの判定部分を != 1 とする
bool isCcwConvex(const VP& ps) {
  int n = ps.size();
  rep (i, n) if (ccw(ps[i], ps[(i+1) % n], ps[(i+2) % n]) == -1) return false;
  return true;
}
 
// 凸多角形の内部判定　O(n)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inConvex(P p, const VP& ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep (i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc) return 2;  // 線分上に存在
    if (ccwc != dir) return 0;
  }
  return 1;
}
// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(const VP& ps, P p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}
 
// 凸多角形クリッピング
VP convexCut(const VP& ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i,n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1) ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1) ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP&);
bool isec_CS(const VP& ps, P a1, P a2){
  rep(i,ps.size()){
    if(isecLS(ps[i],ps[(i+1)%ps.size()],a1,a2))return true;
  }
  return false;
}

void divide_convex(VP ps,const VP &s1,const VP &s2,Vcon &V){
  bool flag = false;
  for(int i=0;i<s1.size();i++){
    VP convex_1=convexCut(ps,s1[i],s2[i]);
    VP convex_2=convexCut(ps,s2[i],s1[i]);
    if(area(convex_1)<EPS||area(convex_2)<EPS)continue;
    divide_convex(convex_1,s1,s2,V);
    divide_convex(convex_2,s1,s2,V);
    flag = true;
    break;
  }
  if(!flag) V.push_back(ps);
  return;
}


// 全く同じ辺を共有してれば１を返す。
bool isecCC(VP a1,VP a2){
  rep(i,a1.size()){
    rep(j,a2.size()){
      P e1=a1[i];
      P e2=a1[(i+1)%a1.size()];
      P e3=a2[j];
      P e4=a2[(j+1)%a2.size()];
      if(abs(e1-e3)<EPS&&abs(e2-e4)<EPS)return 1;
      if(abs(e1-e4)<EPS&&abs(e2-e3)<EPS)return 1;
    }
  }
  return 0;
}

// 頂点抜き
vector<vector<int> > Convex_arrangement(const Vcon& ps){
  int n=ps.size();
  vector<vector<int> >V(n,vector<int>(n));
  rep(i,n)rep(j,n)V[i][j]=1e8;
  rep(i,n){
    for(int j=i+1;j<n;j++){
      if(isecCC(ps[i],ps[j])){
        V[i][j]=1;
        V[j][i]=1;
      }
    }
  }
  return V;
}
 
// 多角形の符号付面積
D area(const VP& ps) {
  D a = 0;
  rep (i, ps.size()) a += cross(ps[i], ps[(i+1) % ps.size()]);
  return a / 2;
}

bool con_in_con(VP a1,VP a2){
  int p=0;
  rep(j,a2.size()){
    int sum=0,f=0;
    if(inPolygon(a1,a2[j])==0)return 0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],a2[j]))goto L1;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],a2[j],P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    L1:;
    sum=f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0))goto L;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0,P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    sum=0;
    L:;
  }
  return !p;
}

signed main(){
  vector<P>s1,s2;
  int n,m,q;
  cin>>n;
  VP V[n];
  rep(i,n){
    cin>>m;
    D x[m],y[m];
    rep(j,m){
      cin>>x[j]>>y[j];
      V[i].push_back(P(x[j],y[j]));
    }
    rep(j,m){
      s1.push_back(P(x[j],y[j]));
      s2.push_back(P(x[(j+1)%m],y[(j+1)%m]));
    }
  }
 
  VP convex;
  convex.push_back(P(-200000,-200000));
  convex.push_back(P(200000,-200000));
  convex.push_back(P(200000,200000));
  convex.push_back(P(-200000,200000));
  Vcon v;
 
  divide_convex(convex,s1,s2,v);
  vector<vector<int> > a=Convex_arrangement(v);
 
  int cnt[v.size()]={};
 
  rep(i,v.size()){
    rep(j,n){
      if(con_in_con(V[j],v[i])){
        cnt[i]++;
      }
    }
  }
  rep(i,v.size()){
    rep(j,v.size()){
      if(i==j)a[i][j]=0;
      if(a[i][j]==1){
        a[i][j]=abs(cnt[i]-cnt[j]);
      }
    }
  }
 
  rep(k,v.size())
    rep(i,v.size())
      rep(j,v.size())
        a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
 
  cin>>q;
  while(q--){
    int A=1e9,B=1e9;
    D a1,a2,b1,b2;
    cin>>a1>>a2>>b1>>b2;
    rep(i,v.size()){
      if(inPolygon(v[i],P(a1,a2))){
        A=i;
        if(inPolygon(v[i],P(a1,a2))==1)break;
      }
    }
    rep(i,v.size()){
      if(inPolygon(v[i],P(b1,b2))){
        B=i;
        if(inPolygon(v[i],P(b1,b2))==1)break;
      }
    }
    cout<<a[A][B]<<endl;
  }
}
accuracy: 0.5319148936170213
time_ms: 10
memory_kb: 3372
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i < (n); i++)
typedef double D;      // 座標値の型。doubleかlong doubleを想定
typedef complex<D> P;  // Point
typedef pair<P, P> L;  // Line
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7;    // 許容誤差。問題によって変える
#define X real()
#define Y imag()
#define LE(n,m) ((n) < (m) + EPS)
#define GE(n,m) ((n) + EPS > (m))
#define EQ(n,m) (abs((n)-(m)) < EPS)
 
// 内積　dot(a,b) = |a||b|cosθ
D dot(P a, P b) {return (conj(a)*b).X; }
 
// 外積　cross(a,b) = |a||b|sinθ
D cross(P a, P b) {  return (conj(a)*b).Y;}
 
// 点の進行方向
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 isparallel(L a,L b){
  return cross(a.first-a.second,b.first-b.second) == 0.0 ;
}
 
/* 交差判定　直線・線分は縮退してはならない。接する場合は交差するとみなす。isecはintersectの略 */
 
// 直線と点
bool isecLP(P a1, P a2, P b) {
  return abs(ccw(a1, a2, b)) != 1;  // return EQ(cross(a2-a1, b-a1), 0); と等価
}
 
// 直線と直線
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2-a1, b2-b1, 0) || isecLP(a1, b1, b2);
}
 
// 直線と線分
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2-a1, b1-a1) * cross(a2-a1, b2-a1) < EPS;
}
 
// 線分と線分
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;
}
 
// 線分と点
bool isecSP(P a1, P a2, P b) {
  //return !ccw(a1, a2, b);
   return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;  //Perfective
}
 

P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2-b1, b1-a1);
  D d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  if (EQ(d2, 0)) throw "kouten ga nai";   // 交点がない
  return a1 + d1/d2 * (a2-a1);
}
namespace std {
  bool operator<(const P a, const P b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}
 
// 凸包
VP convexHull(VP ps) {  // 元の点集合がソートされていいならVP&に
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;  // 余計な点も含むなら == -1 とする
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}
 
// 凸判定。縮退を認めないならccwの判定部分を != 1 とする
bool isCcwConvex(const VP& ps) {
  int n = ps.size();
  rep (i, n) if (ccw(ps[i], ps[(i+1) % n], ps[(i+2) % n]) == -1) return false;
  return true;
}
 
// 凸多角形の内部判定　O(n)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inConvex(P p, const VP& ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep (i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc) return 2;  // 線分上に存在
    if (ccwc != dir) return 0;
  }
  return 1;
}
// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(const VP& ps, P p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}
 
// 凸多角形クリッピング
VP convexCut(const VP& ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i,n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1) ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1) ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP&);
bool isec_CS(const VP& ps, P a1, P a2){
  rep(i,ps.size()){
    if(isecLS(ps[i],ps[(i+1)%ps.size()],a1,a2))return true;
  }
  return false;
}

void divide_convex(VP ps,const VP &s1,const VP &s2,Vcon &V){
  bool flag = false;
  for(int i=0;i<s1.size();i++){
    VP convex_1=convexCut(ps,s1[i],s2[i]);
    VP convex_2=convexCut(ps,s2[i],s1[i]);
    if(area(convex_1)<EPS||area(convex_2)<EPS)continue;
    divide_convex(convex_1,s1,s2,V);
    divide_convex(convex_2,s1,s2,V);
    flag = true;
    break;
  }
  if(!flag) V.push_back(ps);
  return;
}


// 全く同じ辺を共有してれば１を返す。
bool isecCC(VP a1,VP a2){
  rep(i,a1.size()){
    rep(j,a2.size()){
      P e1=a1[i];
      P e2=a1[(i+1)%a1.size()];
      P e3=a2[j];
      P e4=a2[(j+1)%a2.size()];
      if(abs(e1-e3)<EPS&&abs(e2-e4)<EPS)return 1;
      if(abs(e1-e4)<EPS&&abs(e2-e3)<EPS)return 1;
    }
  }
  return 0;
}

// 頂点抜き
vector<vector<int> > Convex_arrangement(const Vcon& ps){
  int n=ps.size();
  vector<vector<int> >V(n,vector<int>(n));
  rep(i,n)rep(j,n)V[i][j]=1e8;
  rep(i,n){
    for(int j=i+1;j<n;j++){
      if(isecCC(ps[i],ps[j])){
        V[i][j]=1;
        V[j][i]=1;
      }
    }
  }
  return V;
}
 
// 多角形の符号付面積
D area(const VP& ps) {
  D a = 0;
  rep (i, ps.size()) a += cross(ps[i], ps[(i+1) % ps.size()]);
  return a / 2;
}

bool con_in_con(VP a1,VP a2){
  int p=0;
  rep(j,a2.size()){
    int sum=0,f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],a2[j]))goto L1;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],a2[j],P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    L1:;
    sum=f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0))goto L;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0,P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    sum=0;
    L:;
  }
  return !p;
}

signed main(){
  vector<P>s1,s2;
  int n,m,q;
  cin>>n;
  VP V[n];
  rep(i,n){
    cin>>m;
    D x[m],y[m];
    rep(j,m){
      cin>>x[j]>>y[j];
      V[i].push_back(P(x[j],y[j]));
    }
    rep(j,m){
      s1.push_back(P(x[j],y[j]));
      s2.push_back(P(x[(j+1)%m],y[(j+1)%m]));
    }
  }

  VP convex;
  convex.push_back(P(-200000,-200000));
  convex.push_back(P(200000,-200000));
  convex.push_back(P(200000,200000));
  convex.push_back(P(-200000,200000));
  Vcon v;

  divide_convex(convex,s1,s2,v);
  vector<vector<int> > a=Convex_arrangement(v);

  int cnt[v.size()]={};

  rep(i,v.size()){
    rep(j,n){
      if(con_in_con(V[j],v[i])){
        cnt[i]++;
      }
    }
  }
  rep(i,v.size()){
    rep(j,v.size()){
      if(i==j)a[i][j]=0;
      if(a[i][j]==1){
        a[i][j]=abs(cnt[i]-cnt[j]);
      }
    }
  }

  rep(k,v.size())
    rep(i,v.size())
      rep(j,v.size())
        a[i][j]=min(a[i][j],a[i][k]+a[k][j]);

  cin>>q;
  while(q--){
    int A=1e9,B=1e9;
    D a1,a2,b1,b2;
    cin>>a1>>a2>>b1>>b2;
    rep(i,v.size()){
      if(inPolygon(v[i],P(a1,a2))){
        A=i;
        if(inPolygon(v[i],P(a1,a2))==1)break;
      }
    }
    rep(i,v.size()){
      if(inPolygon(v[i],P(b1,b2))){
        B=i;
        if(inPolygon(v[i],P(b1,b2))==1)break;
      }
    }
    cout<<a[A][B]<<endl;
  }
}
accuracy: 1
time_ms: 60
memory_kb: 3612
final_rank: 16

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i=0; i < (n); i++)
typedef double D;      // 座標値の型。doubleかlong doubleを想定
typedef complex<D> P;  // Point
typedef pair<P, P> L;  // Line
typedef vector<P> VP;
typedef vector<VP> Vcon;
const D EPS = 1e-7;    // 許容誤差。問題によって変える
#define X real()
#define Y imag()
#define LE(n,m) ((n) < (m) + EPS)
#define GE(n,m) ((n) + EPS > (m))
#define EQ(n,m) (abs((n)-(m)) < EPS)
 
// 内積　dot(a,b) = |a||b|cosθ
D dot(P a, P b) {return (conj(a)*b).X; }
 
// 外積　cross(a,b) = |a||b|sinθ
D cross(P a, P b) {  return (conj(a)*b).Y;}
 
// 点の進行方向
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 isparallel(L a,L b){
  return cross(a.first-a.second,b.first-b.second) == 0.0 ;
}
 
/* 交差判定　直線・線分は縮退してはならない。接する場合は交差するとみなす。isecはintersectの略 */
 
// 直線と点
bool isecLP(P a1, P a2, P b) {
  return abs(ccw(a1, a2, b)) != 1;  // return EQ(cross(a2-a1, b-a1), 0); と等価
}
 
// 直線と直線
bool isecLL(P a1, P a2, P b1, P b2) {
  return !isecLP(a2-a1, b2-b1, 0) || isecLP(a1, b1, b2);
}
 
// 直線と線分
bool isecLS(P a1, P a2, P b1, P b2) {
  return cross(a2-a1, b1-a1) * cross(a2-a1, b2-a1) < EPS;
}
 
// 線分と線分
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;
}
 
// 線分と点
bool isecSP(P a1, P a2, P b) {
  //return !ccw(a1, a2, b);
   return abs(a1 - b) + abs(a2 - b) - abs(a2 - a1) < EPS;  //Perfective
}
 
 
/* 距離　各直線・線分は縮退してはならない */
 
// 点pの直線aへの射影点を返す
P proj(P a1, P a2, P p) {
  return a1 + dot(a2-a1, p-a1)/norm(a2-a1) * (a2-a1);
}
 
// 点pの直線aへの反射点を返す
P reflection(P a1, P a2, P p) {
  return 2.0*proj(a1, a2, p) - p;
}
 
D distLP(P a1, P a2, P p) {
  return abs(proj(a1, a2, p) - p);
}
 
D distLL(P a1, P a2, P b1, P b2) {
  return isecLL(a1, a2, b1, b2) ? 0 : distLP(a1, a2, b1);
}
 
D distLS(P a1, P a2, P b1, P b2) {
  return isecLS(a1, a2, b1, b2) ? 0 : min(distLP(a1, a2, b1), distLP(a1, a2, b2));
}
 
D distSP(P a1, P a2, P p) {
  P r = proj(a1, a2, p);
  if (isecSP(a1, a2, r)) return abs(r-p);
  return min(abs(a1-p), abs(a2-p));
}
 
D distSS(P a1, P a2, P b1, P b2) {
  if (isecSS(a1, a2, b1, b2)) return 0;
  return min(min(distSP(a1, a2, b1), distSP(a1, a2, b2)),
             min(distSP(b1, b2, a1), distSP(b1, b2, a2)));
}
 
// 2直線の交点
P crosspointLL(P a1, P a2, P b1, P b2) {
  D d1 = cross(b2-b1, b1-a1);
  D d2 = cross(b2-b1, a2-a1);
  if (EQ(d1, 0) && EQ(d2, 0)) return a1;  // same line
  if (EQ(d2, 0)) throw "kouten ga nai";   // 交点がない
  return a1 + d1/d2 * (a2-a1);
}
 
 
/* 円 */
 
D distLC(P a1, P a2, P c, D r) {
  return max(distLP(a1, a2, c) - r, 0.0);
}
 
D distSC(P a1, P a2, P c, D r) {
  D dSqr1 = norm(c-a1), dSqr2 = norm(c-a2);
  if (dSqr1 < r*r ^ dSqr2 < r*r) return 0;  // 円が線分を包含するとき距離0ならここをORに変える
  if (dSqr1 < r*r & dSqr2 < r*r) return r - sqrt(max(dSqr1, dSqr2));
  return max(distSP(a1, a2, c) - r, 0.0);
}
 
VP crosspointLC(P a1, P a2, P c, D r) {
  VP ps;
  P ft = proj(a1, a2, c);
  if (!GE(r*r, norm(ft-c))) return ps;
 
  P dir = sqrt(max(r*r - norm(ft-c), 0.0)) / abs(a2-a1) * (a2-a1);
  ps.push_back(ft + dir);
  if (!EQ(r*r, norm(ft-c))) ps.push_back(ft - dir);
  return ps;
}
 
D distCC(P a, D ar, P b, D br) {
  D d = abs(a-b);
  return GE(d, abs(ar-br)) ? max(d-ar-br, 0.0) : abs(ar-br) - d;
}
 
// 2円の交点
VP crosspointCC(P a, D ar, P b, D br) {
  VP ps;
  P ab = b-a;
  D d = abs(ab);
  D crL = (norm(ab) + ar*ar - br*br) / (2*d);
  if (EQ(d, 0) || ar < abs(crL)) return ps;
 
  P abN = ab * P(0, sqrt(ar*ar - crL*crL) / d);
  P cp = a + crL/d * ab;
  ps.push_back(cp + abN);
  if (!EQ(norm(abN), 0)) ps.push_back(cp - abN);
  return ps;
}
 
// 点pから円aへの接線の接点
VP tangentPoints(P a, D ar, P p) {
  VP ps;
  D sin = ar / abs(p-a);
  if (!LE(sin, 1)) return ps;  // ここでNaNも弾かれる
  D t = M_PI_2 - asin(min(sin, 1.0));
  ps.push_back(                 a + (p-a)*polar(sin, t));
  if (!EQ(sin, 1)) ps.push_back(a + (p-a)*polar(sin, -t));
  return ps;
}
 
// 2円の共通接線。返される各直線に含まれる頂点は円との接点となる
vector<L> tangentLines(P a, D ar, P b, D br) {
  vector<L> ls;
  D d = abs(b-a);
  rep (i,2) {
    D sin = (ar - (1-i*2)*br) / d;
    if (!LE(sin*sin, 1)) break;
    D cos = sqrt(max(1 - sin*sin, 0.0));
    rep (j,2) {
      P n = (b-a) * P(sin, (1-j*2)*cos) / d;
      ls.push_back(L(a + ar*n, b + (1-i*2)*br*n));
      if (cos < EPS) break;  // 重複する接線を無視（重複していいならこの行不要）
    }
  }
  return ls;
}
 
// 三角形の外心。点a,b,cは同一線上にあってはならない
P circumcenter(P a, P b, P c) {
  a = (a-c)*0.5;
  b = (b-c)*0.5;
  return c + crosspointLL(a, a*P(1,1), b, b*P(1,1));
}
 
// 点aと点bを通り、半径がrの円の中心を返す
VP circlesPointsRadius(P a, P b, D r) {
  VP cs;
  P abH = (b-a)*0.5;
  D d = abs(abH);
  if (d == 0 || d > r) return cs;  // 必要なら !LE(d,r) として円1つになる側へ丸める
  D dN = sqrt(r*r - d*d);          // 必要なら max(r*r - d*d, 0) とする
  P n = abH * P(0,1) * (dN / d);
  cs.push_back(a + abH + n);
  if (dN > 0) cs.push_back(a + abH - n);
  return cs;
}
 
// 点aと点bを通り、直線lに接する円の中心
VP circlesPointsTangent(P a, P b, P l1, P l2) {
  P n = (l2-l1) * P(0,1);
  P m = (b-a) * P(0,0.5);
  D rC = dot((a+b)*0.5-l1, n);
  D qa = norm(n)*norm(m) - dot(n,m)*dot(n,m);
  D qb = -rC * dot(n,m);
  D qc = norm(n)*norm(m) - rC*rC;
  D qd = qb*qb - qa*qc;  // qa*k^2 + 2*qb*k + qc = 0
 
  VP cs;
  if (qd < -EPS) return cs;
  if (EQ(qa, 0)) {
    if (!EQ(qb, 0)) cs.push_back((a+b)*0.5 - m * (qc/qb/2));
    return cs;
  }
  D t = -qb/qa;
  cs.push_back(              (a+b)*0.5 + m * (t + sqrt(max(qd, 0.0))/qa));
  if (qd > EPS) cs.push_back((a+b)*0.5 + m * (t - sqrt(max(qd, 0.0))/qa));
  return cs;
}
 
// 点集合を含む最小の円の中心
P minEnclosingCircle(const VP& ps) {
  P c;
  double move = 0.5;
  rep(i,39) {  // 2^(-39-1) \approx 0.9e-12
    rep(t,50) {
      D max = 0;
      int k = 0;
      rep (j, ps.size()) if (max < norm(ps[j]-c)) {
        max = norm(ps[j]-c);
        k = j;
      }
      c += (ps[k]-c) * move;
    }
    move /= 2;
  }
  return c;
}
 
// 角度計算
// p1を原点とした時にpをcounter clockwiseで回転させた座標
// D radi=acos((a*a+b*b-c*c)/(2*a*b));
// radi=radi/M_PI*180;   /---これ忘れないで---/
/*
P rot(P p, double a,P p1){
  a=a*M_PI/180.0;
  p-=p1;
  return P(p.X*cos(a)+p.Y*(-sin(a))+x,p.X*sin(a)+p.Y*cos(a)+y);
}
*/
// (a*a+b*b-c*c)/(2*a*b) = cos X 余弦定理
// acos(X) = ラジアンー＞角度

/* 多角形 */
 
// 頂点の順序（sortやmax_elementに必要）
namespace std {
  bool operator<(const P a, const P b) {
    return a.X != b.X ? a.X < b.X : a.Y < b.Y;
  }
}
 
// 凸包
VP convexHull(VP ps) {  // 元の点集合がソートされていいならVP&に
  int n = ps.size(), k = 0;
  sort(ps.begin(), ps.end());
  VP ch(2*n);
  for (int i = 0; i < n; ch[k++] = ps[i++]) // lower-hull
    while (k >= 2 && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;  // 余計な点も含むなら == -1 とする
  for (int i = n-2, t = k+1; i >= 0; ch[k++] = ps[i--]) // upper-hull
    while (k >= t && ccw(ch[k-2], ch[k-1], ps[i]) <= 0) --k;
  ch.resize(k-1);
  return ch;
}
 
// 凸判定。縮退を認めないならccwの判定部分を != 1 とする
bool isCcwConvex(const VP& ps) {
  int n = ps.size();
  rep (i, n) if (ccw(ps[i], ps[(i+1) % n], ps[(i+2) % n]) == -1) return false;
  return true;
}
 
// 凸多角形の内部判定　O(n)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inConvex(P p, const VP& ps) {
  int n = ps.size();
  int dir = ccw(ps[0], ps[1], p);
  rep (i, n) {
    int ccwc = ccw(ps[i], ps[(i + 1) % n], p);
    if (!ccwc) return 2;  // 線分上に存在
    if (ccwc != dir) return 0;
  }
  return 1;
}
 
// 凸多角形の内部判定　O(logn)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inCcwConvex(const VP& ps, P p) {
  int n = ps.size();
  P g = (ps[0] + ps[n / 3] + ps[n*2 / 3]) / 3.0;
  if (g == p) return 1;
  P gp = p - g;
 
  int l = 0, r = n;
  while (l + 1 < r) {
    int mid = (l + r) / 2;
    P gl = ps[l] - g;
    P gm = ps[mid] - g;
    if (cross(gl, gm) > 0) {
      if (cross(gl, gp) >= 0 && cross(gm, gp) <= 0) r = mid;
      else l = mid;
    }
    else {
      if (cross(gl, gp) <= 0 && cross(gm, gp) >= 0) l = mid;
      else r = mid;
    }
  }
  r %= n;
  D cr = cross(ps[l] - p, ps[r] - p);
  return EQ(cr, 0) ? 2 : cr < 0 ? 0 : 1;
}
 
// 多角形の内部判定
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inPolygon(const VP& ps, P p) {
  int n = ps.size();
  bool in = false;
  rep (i, n) {
    P a = ps[i] - p;
    P b = ps[(i + 1) % n] - p;
    if (EQ(cross(a,b), 0) && LE(dot(a,b), 0)) return 2;
    if (a.Y > b.Y) swap(a,b);
    if ((a.Y*b.Y < 0 || (a.Y*b.Y < EPS && b.Y > EPS)) && LE(cross(a, b), 0)) in = !in;
  }
  return in;
}
 
// 凸多角形クリッピング
VP convexCut(const VP& ps, P a1, P a2) {
  int n = ps.size();
  VP ret;
  rep(i,n) {
    int ccwc = ccw(a1, a2, ps[i]);
    if (ccwc != -1) ret.push_back(ps[i]);
    int ccwn = ccw(a1, a2, ps[(i + 1) % n]);
    if (ccwc * ccwn == -1) ret.push_back(crosspointLL(a1, a2, ps[i], ps[(i + 1) % n]));
  }
  return ret;
}
D area(const VP&);
bool isec_CS(const VP& ps, P a1, P a2){
  rep(i,ps.size()){
    if(isecLS(ps[i],ps[(i+1)%ps.size()],a1,a2))return true;
  }
  return false;
}

// a1>a2 //ここ
bool con_in_con(VP a1,VP a2){
  int p=0;
  rep(j,a2.size()){
    int sum=0,f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],a2[j]))goto L1;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],a2[j],P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    L1:;
    sum=f=0;
    rep(i,a1.size()){
      if(isecSP(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0))goto L;
      if(isecSS(a1[i],a1[(i+1)%a1.size()],(a2[j]+a2[(j+1)%a2.size()])/2.0,P(1000007,41321))){
        sum++;
        f++;
      }
    }
    p++;
    if((sum%2))return 1;
    if((sum%2==0))return 0;
    sum=0;
    L:;
  }
  return !p;
  //return false;
}

// 外周の凸多角形の頂点が線分の延長戦にある場合注意
bool Can_convexcut(const VP& ps, P a1, P a2){
  VP v1=convexCut(ps,a1,a2);
  VP v2=convexCut(ps,a2,a1);
  if(area(v1)>EPS&&area(v2)>EPS)return 1;
  return 0;
  rep(i,ps.size()){
    if(isecLS(a1,a2,ps[i],ps[(i+1)%ps.size()])){
      VP v1=convexCut(ps,a1,a2);
      if(isecLP(a1,a2,ps[i]))continue;
      if(isecLP(a1,a2,ps[(i+1)%ps.size()]))continue;
      return true;
    }
  }
  return false;
}


void divide_convex(VP ps,const VP &s1,const VP &s2,Vcon &V){
  //cout<<ps.size()<<endl;
  bool flag = false;
  for(int i=0;i<s1.size();i++){
    if(Can_convexcut(ps,s1[i],s2[i])){
      VP convex_1=convexCut(ps,s1[i],s2[i]);
      VP convex_2=convexCut(ps,s2[i],s1[i]);
      divide_convex(convex_1,s1,s2,V);
      divide_convex(convex_2,s1,s2,V);
      flag = true;
      break;
    }
  }
  if(!flag) V.push_back(ps);
  return;
}

bool isecCC(VP a1,VP a2){

  rep(i,a1.size()){
    rep(j,a2.size()){
      P e1=a1[i];
      P e2=a1[(i+1)%a1.size()];
      P e3=a2[j];
      P e4=a2[(j+1)%a2.size()];
      if(abs(e1-e3)<EPS&&abs(e2-e4)<EPS)return 1;
      if(abs(e1-e4)<EPS&&abs(e2-e3)<EPS)return 1;
      //return 1;
      //if(a1[i]==a2[(j+1)%a2.size()])return 1;
      //if(a1[(i+1)%a1.size()]==a2[(j+1)%a2.size()])return 1;
    }
  }
  return 0;
}

// 頂点抜き
vector<vector<int> > Convex_arrangement(const Vcon& ps){
  int n=ps.size();
  vector<vector<int> >V(n,vector<int>(n));
  rep(i,n)rep(j,n)V[i][j]=1e8;
  rep(i,n){
    for(int j=i+1;j<n;j++){
      if(isecCC(ps[i],ps[j])){
        V[i][j]=1;
        V[j][i]=1;
      }
    }
  }
  return V;
}
 
// 凸多角形の直径（最遠点対）
pair<int, int> convexDiameter(const VP& ps) {
  int n = ps.size();
  int i = min_element(ps.begin(), ps.end()) - ps.begin();
  int j = max_element(ps.begin(), ps.end()) - ps.begin();
  int maxI, maxJ;
  D maxD = 0;
  rep(_, 2*n) {
    if (maxD < norm(ps[i]-ps[j])) {
      maxD = norm(ps[i]-ps[j]);
      maxI = i;
      maxJ = j;
    }
    if (cross(ps[i]-ps[(i+1) % n], ps[(j+1) % n]-ps[j]) <= 0) j = (j+1) % n;
    else                                                      i = (i+1) % n;
  }
  return make_pair(maxI, maxJ);
}
 
// 多角形の符号付面積
D area(const VP& ps) {
  D a = 0;
  rep (i, ps.size()) a += cross(ps[i], ps[(i+1) % ps.size()]);
  return a / 2;
}
 
// 多角形の幾何学的重心
P centroid(const VP& ps) {
  int n = ps.size();
  D aSum = 0;
  P c;
  rep (i, n) {
    D a = cross(ps[i], ps[(i+1) % n]);
    aSum += a;
    c += (ps[i] + ps[(i+1) % n]) * a;
  }
  return 1 / aSum / 3 * c;
}
 
// ボロノイ領域
VP voronoiCell(P p, const VP& ps, const VP& outer) {
  VP cl = outer;
  rep (i, ps.size()) {
    if (EQ(norm(ps[i]-p), 0)) continue;
    P h = (p+ps[i])*0.5;
    cl = convexCut(cl, h, h + (ps[i]-h)*P(0,1) );
  }
  return cl;
}
 
/* 幾何グラフ */
 
struct Edge {
  int from, to;
  D cost;
  Edge(int from, int to, D cost) : from(from), to(to), cost(cost) {}
};
struct Graph {
  int n;
  vector<vector<Edge> > edges;
  Graph(int n) : n(n), edges(n) {}
  void addEdge(Edge e) {
    edges[e.from].push_back(e);
    edges[e.to].push_back(Edge(e.to, e.from, e.cost));
  }
};
 
// 線分アレンジメント（線分の位置関係からグラフを作成）
Graph segmentArrangement(const vector<L>& segs, VP& ps) {
  int n = segs.size();
  rep (i, n) {
    ps.push_back(segs[i].first);
    ps.push_back(segs[i].second);
    rep (j, i) {
      if (isecSS(                 segs[i].first, segs[i].second, segs[j].first, segs[j].second))
        ps.push_back(crosspointLL(segs[i].first, segs[i].second, segs[j].first, segs[j].second));
    }
  }
  sort(ps.begin(), ps.end());
  ps.erase(unique(ps.begin(), ps.end()), ps.end());
 
  int m = ps.size();
  Graph gr(m);
  vector<pair<D, int> > list;
  rep (i, n) {
    list.clear();
    rep (j, m) {
      if (isecSP(segs[i].first, segs[i].second, ps[j]))
        list.push_back(make_pair(norm(segs[i].first-ps[j]), j));
    }
    sort(list.begin(), list.end());
    rep (j, list.size() - 1) {
      int a = list[j  ].second;
      int b = list[j+1].second;
      gr.addEdge(Edge(a, b, abs(ps[a]-ps[b])));
    }
  }
  return gr;
}
 
// 可視グラフ（点集合から見える位置へ辺を張ったグラフ）
Graph visibilityGraph(const VP& ps, const vector<VP>& objs) {
  int n = ps.size();
  Graph gr(n);
  rep (i,n) rep (j,i) {
    P a = ps[i], b = ps[j];
    if (!EQ(norm(a-b), 0)) rep (k, objs.size()) {
      const VP& obj = objs[k];
      int inStA = inConvex(a, obj);
      int inStB = inConvex(b, obj);
      if ((inStA ^ inStB) % 2 || inStA * inStB != 1 && inConvex((a+b)*0.5, obj) == 1) goto skip;
      rep (l, obj.size()) {
        P cur = obj[l];
        P next = obj[(l + 1) % obj.size()];
        if (isecSS(a, b, cur, next) && !isecSP(cur, next, a) && !isecSP(cur, next, b)) goto skip;
      }
    }
    gr.addEdge( Edge(i, j, abs(a-b)) );
    skip: {}
  }
  return gr;
}
 
 
/* その他 */
 
// 重複する線分を併合する
vector<L> mergeSegments(vector<L> segs) {
  int n = segs.size();
  rep (i,n) if (segs[i].second < segs[i].first) swap(segs[i].second, segs[i].first);
 
  rep (i,n) rep (j,i) {
    L &l1 = segs[i], &l2 = segs[j];
    if (EQ(cross(l1.second-l1.first, l2.second-l2.first), 0)
        && isecLP(l1.first, l1.second, l2.first)
        && ccw   (l1.first, l1.second, l2.second) != 2
        && ccw   (l2.first, l2.second, l1.second) != 2) {
      segs[j] = L(min(l1.first, l2.first), max(l1.second, l2.second));
      segs[i--] = segs[--n];
      break;
    }
  }
  segs.resize(n);
  return segs;
}


signed main(){
  vector<P>s1,s2;
  int n,m;
  cin>>n;
  VP V[n];
  rep(i,n){
    cin>>m;
    D x[m],y[m];
    rep(j,m){
      cin>>x[j]>>y[j];
      x[j]+=5007;
      y[j]+=5007;
      V[i].push_back(P(x[j],y[j]));// convex-fullして頂点順序を並び替えた方がいいかも
    }
    //V[i]=convexHull(V[i]);
    rep(j,m){
      s1.push_back(P(x[j],y[j]));
      s2.push_back(P(x[(j+1)%m],y[(j+1)%m]));
    }
  }
  VP convex;
  convex.push_back(P(11,1100));
  convex.push_back(P(10000007,1100));
  convex.push_back(P(10000007,100007));
  convex.push_back(P(11,100007));
  Vcon v;
  divide_convex(convex,s1,s2,v);
  vector<vector<int> > a=Convex_arrangement(v);
  /*cout<<v.size()<<endl;
  rep(i,v.size()){
    rep(j,v.size()){
      cout<<(a[i][j]==1e8?0:1)<<' ';
    }
    cout<<endl;
  }*/
  int cnt[v.size()]={};
  rep(i,v.size()){
    rep(j,n){
      if(con_in_con(V[j],v[i])){
        cnt[i]++;
      }
    }
  }
  /*rep(i,v.size()){
    cout<<i<<endl;
    cout<<v.size()<<endl;
    cout<<cnt[i]<<endl;
    rep(j,v[i].size()){
      cout<<v[i][j].X-5007<<' '<<v[i][j].Y-5007<<endl;
    }
    cout<<endl;
  }*/
  //rep(i,v.size())cout<<cnt[i]<<endl;
  
  // 入力は凸多角形じゃない
  rep(i,v.size()){
    rep(j,v.size()){
      if(i==j)a[i][j]=0;
      if(a[i][j]==1){
        a[i][j]=abs(cnt[i]-cnt[j]);
      }
      //else a[i][j]=0;
    }
  }

  rep(k,v.size())
    rep(i,v.size())
      rep(j,v.size())
        a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
  int q;
  cin>>q;
  while(q--){
    int A=1e9,B=1e9;
    D a1,a2,b1,b2;
    cin>>a1>>a2>>b1>>b2;
    a1+=5007;
    a2+=5007;
    b1+=5007;
    b2+=5007;
    rep(i,v.size()){
      if(inPolygon(v[i],P(a1,a2))==1){
        A=i;
        break;
      }
    }
    if(A==1e9){
      rep(i,v.size()){
        if(inPolygon(v[i],P(a1,a2))==2){
          A=i;
          break;
        }
      }
    }
    rep(i,v.size()){
      if(inPolygon(v[i],P(b1,b2))==1){
        B=i;
        break;
      }
    }
    if(B==1e9){
      rep(i,v.size()){
        if(inPolygon(v[i],P(b1,b2))==2){
          B=i;
          break;
        }
      }
    }
    //cout<<A<<' '<<B<<endl;
    cout<<a[A][B]<<endl;
  }
  //rep(i,v.size()){
    //rep(j,v.size())cout<<(a[i][j]>=1e7?0:a[i][j])<<' ';cout<<endl;
  //}
  /*rep(i,v.size()){
    rep(j,v.size()){
      cout<<a[i][j]<<' ';
    }
    cout<<endl;
  }*/
}
accuracy: 1
time_ms: 60
memory_kb: 3556
final_rank: 12