Problem p01363 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <queue>

#define rep(i, n) for (int i = 0; i < (n); i++)

using namespace std;

const double EPS = 1e-7;
const double INF = 1e77;
const double PI = acos(-1);

template <class T> struct point {
  T x, y;
  point operator+(const point &a) const { return (point){x + a.x, y + a.y}; }
  point operator-(const point &a) const { return (point){x - a.x, y - a.y}; }
};

template <class T> point<T> operator*(T c, const point<T> &a) {
  return (point<T>){c * a.x, c * a.y};
}

template <class T> point<double> operator/(const point<T> &a, double c) {
  return (point<double>){a.x / c, a.y / c};
}

bool operator==(const point<double> &a, const point<double> &b) {
  return abs(a.x - b.x) < EPS && abs(a.y - b.y) < EPS;
}

bool operator!=(const point<double> &a, const point<double> &b) {
  return abs(a.x - b.x) > EPS || abs(a.y - b.y) > EPS;
}

template <class T> struct line {
  point<T> a, b;
};

template <class T> struct segment {
  point<T> a, b;
  operator line<T>() { return (line<T>){a, b}; }
};

template <class T> struct polygon : vector<point<T>> {
  polygon() {}
  polygon(int n) : vector<point<T>>(n) {}
};

template <class T> T abs2(const point<T> &a) { return a.x * a.x + a.y * a.y; }

template <class T> T dot(const point<T> &a, const point<T> &b) {
  return a.x * b.x + a.y * b.y;
}

template <class T> T cross(const point<T> &a, const point<T> &b) {
  return a.x * b.y - a.y * b.x;
}

template <class T> point<double> proj(const point<T> &p, const line<T> &L) {
  return L.a + dot(p - L.a, L.b - L.a) / abs2(L.b - L.a) * (L.b - L.a);
}

template <class T> double arg(const point<T> &a, const point<T> &b) {
  double ta = atan2(a.y, a.x);
  double tb = atan2(b.y, b.x);
  return tb < ta ? tb - ta + 2 * PI : tb - ta;
}

enum { CCW = 1, CW = -1, ON = 0 };
int ccw(const point<double> &a, const point<double> &b,
        const point<double> &c) {
  double rdir = cross(b - a, c - a);
  if (rdir > EPS)
    return CCW;
  if (rdir < -EPS)
    return CW;
  return ON;
}

template <class T>
point<double> get_intersect(const line<T> &L1, const line<T> &L2) {
  double a1 = cross(L1.b - L1.a, L2.b - L2.a);
  double a2 = cross(L1.b - L1.a, L1.b - L2.a);
  if (abs(a1) < EPS)
    return L1.a; // L1 == L2
  return L2.a + a2 / a1 * (L2.b - L2.a);
}

bool intersect(const line<double> &L, const segment<double> &S) {
  return cross(L.b - L.a, S.a - L.a) * cross(L.b - L.a, S.b - L.a) < EPS;
}

bool intersect(const segment<double> &S1, const segment<double> &S2) {
  if (max(S1.a.x, S1.b.x) + EPS < min(S2.a.x, S2.b.x) ||
      max(S1.a.y, S1.b.y) + EPS < min(S2.a.y, S2.b.y) ||
      max(S2.a.x, S2.b.x) + EPS < min(S1.a.x, S1.b.x) ||
      max(S2.a.y, S2.b.y) + EPS < min(S1.a.y, S1.b.y))
    return false;
  return ccw(S1.a, S1.b, S2.a) * ccw(S1.a, S1.b, S2.b) <= 0 &&
         ccw(S2.a, S2.b, S1.a) * ccw(S2.a, S2.b, S1.b) <= 0;
}

template <class T> double dist(const point<T> &a, const point<T> &b) {
  return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

int n;                     // ビルの数
polygon<double> B[100];    // ビル
point<double> Alice, Bomb; // Alice の位置, 爆弾の位置

// a から b が見えるかどうか ( 視線がビルに遮られていないか ) を判定する
// 見えるとき true を返す
// a, b ともに同じビルの内部にあるときに間違って true を返すけど、そのような a,
// b に対してこの関数を呼び出すことはない
bool is_visible(const point<double> &a, const point<double> &b) {
  segment<double> S = {a, b};
  rep(i, n) rep(j, B[i].size()) {
    int m = B[i].size();
    segment<double> T = {B[i][j], B[i][(j + 1) % m]};

    if (!intersect(S, T))
      continue;

    point<double> p = get_intersect(line<double>(S), line<double>(T));
    // ビルの角以外でぶつかったとき
    if (p != T.a && p != T.b) {
      if (p != a && p != b)
        return false;
      else {
        if (p == a && ccw(b, a, T.a) == CW)
          return false;
        if (p == b && ccw(a, b, T.a) == CW)
          return false;
      }
    }
    // ビルの角でぶつかったとき
    else {
      point<double> q =
          (p == b ? a : b); // a, b のうちビルの角でない方の点 (
                            // どちらもビルの角でなければ b を優先 )
      int k = (p == T.a ? j : (j + 1) % m); // G[i][k]==p なる k
      double t1 = arg(B[i][(k - 1 + m) % m] - p, q - p);
      double t2 = arg(B[i][(k - 1 + m) % m] - p, B[i][(k + 1) % m] - p);
      if (!(t1 < t2 + EPS))
        return false;
    }
  }

  return true;
}

// tar に爆風が届くかどうかを判定する
// 届かなければ true を返す
// tar はビルの内部にないことが保障されている
bool is_goal(const point<double> &tar) {
  point<double> a = Bomb, b = tar;
  if (!is_visible(a, b))
    return true;

  segment<double> S = {a, b};
  rep(i, n) rep(j, B[i].size()) {
    int m = B[i].size();
    segment<double> T = {B[i][j], B[i][(j + 1) % m]};
    if (!intersect(S, T))
      continue;

    point<double> p = get_intersect(line<double>(S), line<double>(T));
    // p == a はありえないことが保障されている
    if (p != b)
      return true;
    else {
      // p!=T.a && p!=T.b のケースは is_visible ですでに検出されている
      if (p == T.a || p == T.b) {
        int k = (p == T.a ? j : (j + 1) % m);
        int c1 = ccw(a, b, B[i][(k - 1 + m) % m]);
        int c2 = ccw(a, b, B[i][(k + 1) % m]);
        if (c1 == ON || c2 == ON || c1 == c2)
          return true;
      }
    }
  }

  return false;
}

struct edge {
  int v;
  double cost;
};

double solve() {
  // 終点候補 1
  // ビルの頂点集合
  vector<point<double>> P(1, Alice);
  rep(i, n) P.insert(P.end(), B[i].begin(), B[i].end());

  // 終点候補 2
  // L : 爆風とビルの境目の直線の集合
  vector<line<double>> L;
  rep(i, n) rep(j, B[i].size()) if (is_visible(Bomb, B[i][j]))
      L.push_back((line<double>){Bomb, B[i][j]});
  // 重複している L の直線を削除
  rep(i, L.size()) {
    rep(j, i) if (ccw(L[i].b, Bomb, L[j].b) == ON) {
      L.erase(L.begin() + i);
      i--;
      break;
    }
  }

  // 終点候補 3
  // R : ビルと L との交点で爆風が届かない点の集合
  vector<point<double>> R;
  rep(i, n) rep(j, B[i].size()) rep(k, L.size()) {
    int m = B[i].size();
    segment<double> S = {B[i][j], B[i][(j + 1) % m]};
    if (intersect(L[k], S)) {
      point<double> p = get_intersect(L[k], line<double>(S));
      if (is_goal(p))
        R.push_back(p);
    }
  }
  // あるビルの頂点に一致する R の点を削除
  rep(i, R.size()) {
    rep(u, P.size()) if (R[i] == P[u]) {
      R.erase(R.begin() + i);
      i--;
      break;
    }
  }
  // 重複している R の点を削除
  rep(i, R.size()) {
    rep(j, i) if (R[i] == R[j]) {
      R.erase(R.begin() + i);
      i--;
      break;
    }
  }

  // Dijkstra
  double ans = INF;
  vector<double> d(P.size(), INF);
  d[0] = 0;
  priority_queue<pair<double, int>> Q;
  Q.push(make_pair(0, 0));
  while (!Q.empty()) {
    double d_now = -Q.top().first;
    int u = Q.top().second;
    Q.pop();

    if (ans < d_now)
      break;
    if (d[u] < d_now)
      continue;

    if (is_goal(P[u])) {
      ans = d[u];
      break;
    }

    // P[u] から L の垂線の足へ移動するケース
    rep(i, L.size()) {
      point<double> p = proj(P[u], L[i]);
      if (is_visible(P[u], p) && is_goal(p))
        ans = min(ans, d[u] + dist(P[u], p));
    }

    // P[u] から R の点へ移動するケース
    rep(i, R.size()) {
      if (is_visible(P[u], R[i]))
        ans = min(ans, d[u] + dist(P[u], R[i]));
    }

    rep(v, P.size()) if (u != v) {
      double cost = dist(P[u], P[v]);
      if (d[v] > d[u] + cost && is_visible(P[u], P[v])) {
        d[v] = d[u] + cost;
        Q.push(make_pair(-d[v], v));
      }
    }
  }

  return ans;
}

int main() {
  Alice.x = Alice.y = 0;
  for (; scanf("%d", &n), n;) {
    scanf("%lf%lf", &Bomb.x, &Bomb.y);
    rep(i, n) {
      int m;
      scanf("%d", &m);
      B[i].resize(m);
      rep(j, m) scanf("%lf%lf", &B[i][j].x, &B[i][j].y);
    }
    printf("%.9f\\n", solve());
  }
  return 0;
}

Error: TLE

Fixed submission:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <queue>

#define rep(i, n) for (int i = 0; i < (n); i++)

using namespace std;

const double EPS = 1e-7;
const double INF = 1e77;
const double PI = acos(-1);

template <class T> struct point {
  T x, y;
  point operator+(const point &a) const { return (point){x + a.x, y + a.y}; }
  point operator-(const point &a) const { return (point){x - a.x, y - a.y}; }
};

template <class T> point<T> operator*(T c, const point<T> &a) {
  return (point<T>){c * a.x, c * a.y};
}

template <class T> point<double> operator/(const point<T> &a, double c) {
  return (point<double>){a.x / c, a.y / c};
}

bool operator==(const point<double> &a, const point<double> &b) {
  return abs(a.x - b.x) < EPS && abs(a.y - b.y) < EPS;
}

bool operator!=(const point<double> &a, const point<double> &b) {
  return abs(a.x - b.x) > EPS || abs(a.y - b.y) > EPS;
}

template <class T> struct line {
  point<T> a, b;
};

template <class T> struct segment {
  point<T> a, b;
  operator line<T>() { return (line<T>){a, b}; }
};

template <class T> struct polygon : vector<point<T>> {
  polygon() {}
  polygon(int n) : vector<point<T>>(n) {}
};

template <class T> T abs2(const point<T> &a) { return a.x * a.x + a.y * a.y; }

template <class T> T dot(const point<T> &a, const point<T> &b) {
  return a.x * b.x + a.y * b.y;
}

template <class T> T cross(const point<T> &a, const point<T> &b) {
  return a.x * b.y - a.y * b.x;
}

template <class T> point<double> proj(const point<T> &p, const line<T> &L) {
  return L.a + dot(p - L.a, L.b - L.a) / abs2(L.b - L.a) * (L.b - L.a);
}

template <class T> double arg(const point<T> &a, const point<T> &b) {
  double ta = atan2(a.y, a.x);
  double tb = atan2(b.y, b.x);
  return tb < ta ? tb - ta + 2 * PI : tb - ta;
}

enum { CCW = 1, CW = -1, ON = 0 };
int ccw(const point<double> &a, const point<double> &b,
        const point<double> &c) {
  double rdir = cross(b - a, c - a);
  if (rdir > EPS)
    return CCW;
  if (rdir < -EPS)
    return CW;
  return ON;
}

template <class T>
point<double> get_intersect(const line<T> &L1, const line<T> &L2) {
  double a1 = cross(L1.b - L1.a, L2.b - L2.a);
  double a2 = cross(L1.b - L1.a, L1.b - L2.a);
  if (abs(a1) < EPS)
    return L1.a; // L1 == L2
  return L2.a + a2 / a1 * (L2.b - L2.a);
}

bool intersect(const line<double> &L, const segment<double> &S) {
  return cross(L.b - L.a, S.a - L.a) * cross(L.b - L.a, S.b - L.a) < EPS;
}

bool intersect(const segment<double> &S1, const segment<double> &S2) {
  if (max(S1.a.x, S1.b.x) + EPS < min(S2.a.x, S2.b.x) ||
      max(S1.a.y, S1.b.y) + EPS < min(S2.a.y, S2.b.y) ||
      max(S2.a.x, S2.b.x) + EPS < min(S1.a.x, S1.b.x) ||
      max(S2.a.y, S2.b.y) + EPS < min(S1.a.y, S1.b.y))
    return false;
  return ccw(S1.a, S1.b, S2.a) * ccw(S1.a, S1.b, S2.b) <= 0 &&
         ccw(S2.a, S2.b, S1.a) * ccw(S2.a, S2.b, S1.b) <= 0;
}

template <class T> double dist(const point<T> &a, const point<T> &b) {
  return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

int n;                     // ビルの数
polygon<double> B[100];    // ビル
point<double> Alice, Bomb; // Alice の位置, 爆弾の位置

// a から b が見えるかどうか ( 視線がビルに遮られていないか ) を判定する
// 見えるとき true を返す
// a, b ともに同じビルの内部にあるときに間違って true を返すけど、そのような a,
// b に対してこの関数を呼び出すことはない
bool is_visible(const point<double> &a, const point<double> &b) {
  segment<double> S = {a, b};
  rep(i, n) rep(j, B[i].size()) {
    int m = B[i].size();
    segment<double> T = {B[i][j], B[i][(j + 1) % m]};

    if (!intersect(S, T))
      continue;

    point<double> p = get_intersect(line<double>(S), line<double>(T));
    // ビルの角以外でぶつかったとき
    if (p != T.a && p != T.b) {
      if (p != a && p != b)
        return false;
      else {
        if (p == a && ccw(b, a, T.a) == CW)
          return false;
        if (p == b && ccw(a, b, T.a) == CW)
          return false;
      }
    }
    // ビルの角でぶつかったとき
    else {
      point<double> q =
          (p == b ? a : b); // a, b のうちビルの角でない方の点 (
                            // どちらもビルの角でなければ b を優先 )
      int k = (p == T.a ? j : (j + 1) % m); // G[i][k]==p なる k
      double t1 = arg(B[i][(k - 1 + m) % m] - p, q - p);
      double t2 = arg(B[i][(k - 1 + m) % m] - p, B[i][(k + 1) % m] - p);
      if (!(t1 < t2 + EPS))
        return false;
    }
  }

  return true;
}

// tar に爆風が届くかどうかを判定する
// 届かなければ true を返す
// tar はビルの内部にないことが保障されている
bool is_goal(const point<double> &tar) {
  point<double> a = Bomb, b = tar;
  if (!is_visible(a, b))
    return true;

  segment<double> S = {a, b};
  rep(i, n) rep(j, B[i].size()) {
    int m = B[i].size();
    segment<double> T = {B[i][j], B[i][(j + 1) % m]};
    if (!intersect(S, T))
      continue;

    point<double> p = get_intersect(line<double>(S), line<double>(T));
    // p == a はありえないことが保障されている
    if (p != b)
      return true;
    else {
      // p!=T.a && p!=T.b のケースは is_visible ですでに検出されている
      if (p == T.a || p == T.b) {
        int k = (p == T.a ? j : (j + 1) % m);
        int c1 = ccw(a, b, B[i][(k - 1 + m) % m]);
        int c2 = ccw(a, b, B[i][(k + 1) % m]);
        if (c1 == ON || c2 == ON || c1 == c2)
          return true;
      }
    }
  }

  return false;
}

struct edge {
  int v;
  double cost;
};

double solve() {
  // 終点候補 1
  // ビルの頂点集合
  vector<point<double>> P(1, Alice);
  rep(i, n) P.insert(P.end(), B[i].begin(), B[i].end());

  // 終点候補 2
  // 爆風とビルの境目の直線
  vector<line<double>> L;
  rep(i, n) rep(j, B[i].size()) if (is_visible(Bomb, B[i][j]))
      L.push_back((line<double>){Bomb, B[i][j]});
  // 重複している L の直線を削除
  rep(i, L.size()) {
    rep(j, i) if (ccw(L[i].b, Bomb, L[j].b) == ON) {
      L.erase(L.begin() + i);
      i--;
      break;
    }
  }

  // 終点候補 3
  // ビルと L との交点で爆風が届かない点の集合
  vector<point<double>> R;
  rep(i, n) rep(j, B[i].size()) rep(k, L.size()) {
    int m = B[i].size();
    segment<double> S = {B[i][j], B[i][(j + 1) % m]};
    if (intersect(L[k], S)) {
      point<double> p = get_intersect(L[k], line<double>(S));
      if (is_goal(p))
        R.push_back(p);
    }
  }
  // あるビルの頂点に一致する R の点を削除
  rep(i, R.size()) {
    rep(u, P.size()) if (R[i] == P[u]) {
      R.erase(R.begin() + i);
      i--;
      break;
    }
  }
  // 重複している R の点を削除
  rep(i, R.size()) {
    rep(j, i) if (R[i] == R[j]) {
      R.erase(R.begin() + i);
      i--;
      break;
    }
  }

  // Dijkstra
  double ans = INF;
  vector<double> d(P.size(), INF);
  d[0] = 0;
  priority_queue<pair<double, int>> Q;
  Q.push(make_pair(0, 0));
  while (!Q.empty()) {
    double d_now = -Q.top().first;
    int u = Q.top().second;
    Q.pop();

    if (ans < d_now)
      break;
    if (d[u] < d_now)
      continue;

    // fprintf(stderr," u = %3d\\n",u);
    if (is_goal(P[u])) {
      ans = d[u];
      break;
    }

    // P[u] から L の垂線の足へ移動するケース
    rep(i, L.size()) {
      point<double> p = proj(P[u], L[i]);
      if (is_visible(P[u], p) && is_goal(p))
        ans = min(ans, d[u] + dist(P[u], p));
    }

    // P[u] から R の点へ移動するケース
    rep(i, R.size()) {
      if (is_visible(P[u], R[i]))
        ans = min(ans, d[u] + dist(P[u], R[i]));
    }

    rep(v, P.size()) if (u != v && is_visible(P[u], P[v])) {
      double cost = dist(P[u], P[v]);
      if (d[v] > d[u] + cost) {
        d[v] = d[u] + cost;
        Q.push(make_pair(-d[v], v));
      }
    }
  }

  return ans;
}

int main() {
  Alice.x = Alice.y = 0;
  for (; scanf("%d", &n), n;) {
    // static int T=1;
    // fprintf(stderr,"T=%3d:\\n",T++);
    scanf("%lf%lf", &Bomb.x, &Bomb.y);
    rep(i, n) {
      int m;
      scanf("%d", &m);
      B[i].resize(m);
      rep(j, m) scanf("%lf%lf", &B[i][j].x, &B[i][j].y);
    }
    printf("%.9f\\n", solve());
  }
  return 0;
}