Problem p00619 (C++) — buggy submission, verdict: Runtime Error

#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
using namespace std;
typedef long long LL;

typedef complex<double> P;
const double EPS = 1e-8;

// 誤差を加味した符号判定
int sign(double x) { return x > EPS ? 1 : x < -EPS ? -1 : 0; }

// 内積・外積
double dot(P a, P b) { return real(conj(a) * b); }
double cross(P a, P b) { return imag(conj(a) * b); }

// OAとOBのなす符号付き角度 [-pi, pi]
// example : (1, 0), (0, 1) -> pi/2
double angle(P a, P b) { return arg(conj(a) * b); }

// aをc中心にb[rad]回転
P rotate(P a, double b, P c = P()) { return (a - c) * polar(1.0, b) + c; }

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; // c--a--b の順番で一直線上
  if (norm(b) + EPS < norm(c))
    return -2; // a--b--c の順番で一直線上
  return 0;    // 点が線分ab上にある
}

enum { OUT, ON, IN };

// Pointの比較をしたいときだけ定義する．
namespace std {
bool operator<(const P &a, const P &b) {
  return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
}
}; // namespace std

typedef vector<P> L;
P vec(L l) { return l[1] - l[0]; }

// 線分と点の交差判定(端点の処理に注意)(端点は含むけれども誤差に注意)
// verify : AOJ1279, AOJ2506
bool iSP(L s, P p) { return ccw(s[0], s[1], p) == 0; }

// 直線と直線の交点
// Verified: AOJ2579
// size()によって場合分け
// *4 : 直線が重なる  *1 : 交点が1つ存在 *0 : 直線が交差しない
vector<P> pLL(L l, L m) {
  double A = cross(vec(l), vec(m));
  double B = cross(vec(l), l[1] - m[0]);
  if (sign(A) == 0 && sign(B) == 0)
    return {l[0], l[1], m[0], m[1]}; // 二直線が重なっている
  if (sign(A) == 0)
    return {}; // 直線が交わらない
  return {m[0] + vec(m) * B / A};
}

vector<P> pSS(L l, L m) {
  vector<P> res;
  auto find = [&](P p) {
    for (P r : res)
      if (sign(abs(r - p)) == 0)
        return true;
    return false;
  };
  for (P p : pLL(l, m))
    if (iSP(l, p) && iSP(m, p) && !find(p)) // 片方が直線の場合は適宜変えること
      res.push_back(p);
  return res;
}

// 入力
// ss : 線分のリスト
//
// 出力
// ps : グラフの頂点番号に対応する点が入る
// 返り値 : 上の説明のグラフ
//
// Verified
// AOJ 2113
typedef vector<int> Node;
typedef vector<Node> Graph;
Graph segment_arrangement(const vector<L> &ss, vector<P> &ps) {
  for (int i = 0; i < ss.size(); i++) {
    ps.push_back(ss[i][0]);
    ps.push_back(ss[i][1]);
    for (int j = i + 1; j < ss.size(); j++) {
      auto cp = pSS(ss[i], ss[j]);
      if (!cp.empty()) {
        assert(cp.size() == 1);
        ps.push_back(cp.back());
      }
    }
  }
  sort(ps.begin(), ps.end());
  ps.erase(unique(ps.begin(), ps.end()), ps.end());

  Graph g(ps.size());
  for (int i = 0; i < ss.size(); i++) {
    vector<int> on;
    for (int j = 0; j < ps.size(); j++) {
      if (iSP(ss[i], ps[j])) {
        on.push_back(j);
      }
    }
    for (int j = 0; j + 1 < on.size(); j++) {
      for (int k = j + 1; k < on.size(); k++) {
        int a = on[j], b = on[k];
        g[a].push_back(b);
        g[b].push_back(a);
      }
    }
  }
  return g;
}

// 線分併合
//
// 線分のリストからオーバーラップするものたちをまとめ,新しい線分のリストを作る.
// 元々の線分のリストにおける順番は破壊される．
//
// not verified
void merge_segments(vector<L> &segs) {
  auto merge_if_able = [](L &s, L t) {
    if (abs(cross(s[1] - s[0], t[1] - t[0])) > EPS)
      return false;
    // if (sign(cross(vec(s), vec(t))) != 0) return false;
    if (ccw(s[0], t[0], s[1]) == +1 || ccw(s[0], t[0], s[1]) == -1)
      return false; // not on the same line
    if (ccw(s[0], s[1], t[0]) == -2 || ccw(t[0], t[1], s[0]) == -2)
      return false; // separated
    s = {min(s[0], t[0]), max(s[1], t[1])};
    return true;
  };
  for (int i = 0; i < segs.size(); ++i)
    if (segs[i][1] < segs[i][0])
      swap(segs[i][1], segs[i][0]);
  for (int i = 0; i < segs.size(); ++i)
    for (int j = i + 1; j < segs.size(); ++j)
      if (merge_if_able(segs[i], segs[j]))
        segs[j--] = segs.back(), segs.pop_back();
}
P input() {
  double x, y;
  cin >> x >> y;
  return {x, y};
}
typedef tuple<double, int, int> T;
const int MAX = 500;
const double INF = 1e64;

int main() {
  int n;
  while (cin >> n && n > 0) {
    vector<L> lines(n);
    REP(i, n) {
      P a = input();
      P b = input();
      lines[i] = {a, b};
    }

    merge_segments(lines);
    // assert(size == lines.size());

    vector<P> ps;
    Graph G = segment_arrangement(lines, ps);
    /*
    REP(i, G.size()){
        cout << i << ":";
        for(int v : G[i]) cout << v << " ";
        cout << endl;
    }
    */
    assert(ps.size() < MAX);

    REP(i, ps.size()) REP(j, i) { assert(abs(ps[i] - ps[j]) > EPS); }

    auto find = [](const vector<P> &pv, P p) -> int {
      for (int i = 0; i < pv.size(); i++) {
        auto q = pv[i];
        if (abs(p - q) < EPS) {
          return i;
        }
      }
      return -1;
    };

    P sp = input();
    P gp = input();
    int s = find(ps, sp);
    int g = find(ps, gp);

    if (s == g) {
      cout << 0 << endl;
      continue;
    }

    assert(s != -1 && g != -1);

    priority_queue<T, vector<T>, greater<T>> que;
    double dist[MAX][MAX] = {};
    REP(i, MAX) REP(j, MAX) dist[i][j] = INF;
    for (int u : G[s]) {
      dist[s][u] = 0.0;
      que.push(T(0.0, s, u));
    }
    double ans = INF;
    while (!que.empty()) {
      T t = que.top();
      que.pop();
      double d;
      int u, v;
      tie(d, u, v) = t;
      if (v == g) {
        ans = d;
        break;
      }
      for (int w : G[v])
        if (u != w) {
          P p = ps[v] - ps[u];
          P q = ps[w] - ps[v];
          double th = abs(angle(p, q)) / M_PI * 180.0;
          double nd = d + th;
          if (dist[v][w] > nd) {
            dist[v][w] = nd;
            que.push(T(nd, v, w));
          }
        }
    }
    if (ans == INF) {
      cout << -1 << endl;
    } else {
      printf("%.12f\\n", ans);
    }
  }

  return 0;
}

Error: WA

Fixed submission:

#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
using namespace std;
typedef long long LL;

typedef complex<double> P;
const double EPS = 1e-8;

// 誤差を加味した符号判定
int sign(double x) { return x > EPS ? 1 : x < -EPS ? -1 : 0; }

// 内積・外積
double dot(P a, P b) { return real(conj(a) * b); }
double cross(P a, P b) { return imag(conj(a) * b); }

// OAとOBのなす符号付き角度 [-pi, pi]
// example : (1, 0), (0, 1) -> pi/2
double angle(P a, P b) { return arg(conj(a) * b); }

// aをc中心にb[rad]回転
P rotate(P a, double b, P c = P()) { return (a - c) * polar(1.0, b) + c; }

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) < 0)
    return +2; // c--a--b の順番で一直線上
  if (norm(b) < norm(c))
    return -2; // a--b--c の順番で一直線上
  return 0;    // 点が線分ab上にある
}

enum { OUT, ON, IN };

// Pointの比較をしたいときだけ定義する．
namespace std {
bool operator<(const P &a, const P &b) {
  return a.real() != b.real() ? a.real() < b.real() : a.imag() < b.imag();
}
}; // namespace std

typedef vector<P> L;
P vec(L l) { return l[1] - l[0]; }

// 線分と点の交差判定(端点の処理に注意)(端点は含むけれども誤差に注意)
// verify : AOJ1279, AOJ2506
bool iSP(L s, P p) { return ccw(s[0], s[1], p) == 0; }

// 直線と直線の交点
// Verified: AOJ2579
// size()によって場合分け
// *4 : 直線が重なる  *1 : 交点が1つ存在 *0 : 直線が交差しない
vector<P> pLL(L l, L m) {
  double A = cross(vec(l), vec(m));
  double B = cross(vec(l), l[1] - m[0]);
  if (sign(A) == 0 && sign(B) == 0)
    return {l[0], l[1], m[0], m[1]}; // 二直線が重なっている
  if (sign(A) == 0)
    return {}; // 直線が交わらない
  return {m[0] + vec(m) * B / A};
}

vector<P> pSS(L l, L m) {
  vector<P> res;
  auto find = [&](P p) {
    for (P r : res)
      if (sign(abs(r - p)) == 0)
        return true;
    return false;
  };
  for (P p : pLL(l, m))
    if (iSP(l, p) && iSP(m, p) && !find(p)) // 片方が直線の場合は適宜変えること
      res.push_back(p);
  return res;
}

// 入力
// ss : 線分のリスト
//
// 出力
// ps : グラフの頂点番号に対応する点が入る
// 返り値 : 上の説明のグラフ
//
// Verified
// AOJ 2113
typedef vector<int> Node;
typedef vector<Node> Graph;
Graph segment_arrangement(const vector<L> &ss, vector<P> &ps) {
  for (int i = 0; i < ss.size(); i++) {
    ps.push_back(ss[i][0]);
    ps.push_back(ss[i][1]);
    for (int j = i + 1; j < ss.size(); j++) {
      auto cp = pSS(ss[i], ss[j]);
      if (!cp.empty()) {
        assert(cp.size() == 1);
        ps.push_back(cp.back());
      }
    }
  }
  sort(ps.begin(), ps.end());
  ps.erase(unique(ps.begin(), ps.end()), ps.end());

  Graph g(ps.size());
  for (int i = 0; i < ss.size(); i++) {
    vector<int> on;
    for (int j = 0; j < ps.size(); j++) {
      if (iSP(ss[i], ps[j])) {
        on.push_back(j);
      }
    }
    for (int j = 0; j + 1 < on.size(); j++) {
      for (int k = j + 1; k < on.size(); k++) {
        int a = on[j], b = on[k];
        g[a].push_back(b);
        g[b].push_back(a);
      }
    }
  }
  return g;
}

// 線分併合
//
// 線分のリストからオーバーラップするものたちをまとめ,新しい線分のリストを作る.
// 元々の線分のリストにおける順番は破壊される．
//
// not verified
void merge_segments(vector<L> &segs) {
  auto merge_if_able = [](L &s, L t) {
    if (sign(cross(vec(s), vec(t))) != 0)
      return false;
    if (ccw(s[0], t[0], s[1]) == +1 || ccw(s[0], t[0], s[1]) == -1)
      return false; // not on the same line
    if (ccw(s[0], s[1], t[0]) == -2 || ccw(t[0], t[1], s[0]) == -2)
      return false; // separated
    s = {min(s[0], t[0]), max(s[1], t[1])};
    return true;
  };
  for (int i = 0; i < segs.size(); ++i)
    if (segs[i][1] < segs[i][0])
      swap(segs[i][1], segs[i][0]);
  for (int i = 0; i < segs.size(); ++i)
    for (int j = i + 1; j < segs.size(); ++j)
      if (merge_if_able(segs[i], segs[j]))
        segs[j--] = segs.back(), segs.pop_back();
}
P input() {
  double x, y;
  cin >> x >> y;
  return {x, y};
}
typedef tuple<double, int, int> T;
const int MAX = 500;
const double INF = 1e64;

int main() {
  int n;
  while (cin >> n && n > 0) {
    vector<L> lines(n);
    REP(i, n) {
      P a = input();
      P b = input();
      lines[i] = {a, b};
    }

    int size = lines.size();
    merge_segments(lines);
    assert(size == lines.size());

    vector<P> ps;
    Graph G = segment_arrangement(lines, ps);
    assert(ps.size() < MAX);

    REP(i, ps.size()) REP(j, i) { assert(abs(ps[i] - ps[j]) > EPS); }

    auto find = [](const vector<P> &pv, P p) -> int {
      for (int i = 0; i < pv.size(); i++) {
        auto q = pv[i];
        if (abs(p - q) < EPS) {
          return i;
        }
      }
      return -1;
    };

    P sp = input();
    P gp = input();
    int s = find(ps, sp);
    int g = find(ps, gp);

    if (s == g) {
      cout << 0 << endl;
      continue;
    }

    assert(s != -1 && g != -1);

    priority_queue<T, vector<T>, greater<T>> que;
    double dist[MAX][MAX] = {};
    REP(i, MAX) REP(j, MAX) dist[i][j] = INF;
    for (int u : G[s]) {
      dist[s][u] = 0.0;
      que.push(T(0.0, s, u));
    }
    double ans = INF;
    while (!que.empty()) {
      T t = que.top();
      que.pop();
      double d;
      int u, v;
      if (v == g) {
        ans = d;
        break;
      }
      tie(d, u, v) = t;
      for (int w : G[v])
        if (u != w) {
          P p = ps[v] - ps[u];
          P q = ps[w] - ps[v];
          double th = abs(angle(p, q)) / M_PI * 180.0;
          double nd = d + th;
          if (dist[v][w] > nd) {
            dist[v][w] = nd;
            que.push(T(nd, v, w));
          }
        }
    }
    if (ans == INF) {
      cout << -1 << endl;
    } else {
      printf("%.12f\\n", ans);
    }
  }

  return 0;
}