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

#include <algorithm>
#include <cassert>
#include <complex>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

#define EQ(x, y) (abs((x) - (y)) < EPS)
#define EPS 1e-8
#define INF 0x33433433

#define X real()
#define Y imag()

// typedef float double;
typedef pair<int, int> Pair;
typedef complex<double> Point;
typedef Point Vec;

namespace std {
bool operator<(Point a, Point b) {
  if (a.X != b.X)
    return a.X < b.X;
  return a.Y < b.Y;
}
} // namespace std

class Line : public vector<Point> {
public:
  Line() { resize(2); }
  Line(Point a, Point b) {
    push_back(a);
    push_back(b);
  }
};

struct Circle {
  Point c;
  double r;
};

double dot(Point a, Point b) { return (conj(a) * b).X; }

double cross(Point a, Point b) { return (conj(a) * b).Y; }

int ccw(Point a, Point b, Point c) {
  b -= a;
  c -= a;
  if (cross(b, c) > 0)
    return 1;
  if (cross(b, c) < 0)
    return -1;
  if (dot(b, c) < 0)
    return 2;
  if (norm(b) < norm(c))
    return -2;
  return 0;
}

Point proj(Line l, Point p) {
  Point v = l[1] - l[0];
  double ratio = dot(v, p - l[0]) / norm(v);
  return l[0] + ratio * v;
}

double DistLP(Line l, Point p) { return abs(p - proj(l, p)); }

vector<Point> CrossPointCL(Circle c, Line l) {
  vector<Point> ret;
  double d = DistLP(l, c.c);
  if (d < c.r + EPS) {
    double ratio = (d > c.r) ? 0.0 : sqrt(c.r * c.r - d * d);
    Point sgn = (l[0] - l[1]) / abs(l[0] - l[1]);
    ret.push_back(proj(l, c.c) + ratio * sgn);
    ret.push_back(proj(l, c.c) - ratio * sgn);
  }

  return ret;
}

vector<Point> CrossPointCC(Circle c1, Circle c2) {
  vector<Point> ret;
  double d = abs(c1.c - c2.c);
  double rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
  double dfr = c1.r * c1.r - rc * rc;

  if (EQ(dfr, 0.0))
    dfr = 0.0;
  else if (dfr < 0.0)
    return ret;

  double rs = sqrt(dfr);
  Point sgn = (c2.c - c1.c) / d;
  ret.push_back(c1.c + sgn * Point(rc, rs));
  if (dfr > 0.0)
    ret.push_back(c1.c + sgn * Point(rc, -rs));
  return ret;
}

int n;
double A, B, C;
Point rocks[50];
int mind[11451419];
bool considered[30][30][30][30];
bool result[30][30][30][30];

void NormalizeNum(int *nums) {
  if (nums[0] > nums[1])
    swap(nums[0], nums[1]);
  if (nums[2] > nums[3])
    swap(nums[2], nums[3]);
}

int CalcState(int *nums, bool *is_goal) {
  int state = 0;
  for (int i = 0; i < 4; i++) {
    state = state * 31 + nums[i];
    if (nums[i] == n && is_goal)
      *is_goal = true;
  }
  return state;
}

void State2Num(int state, int *nums) {
  for (int i = 0; i < 4; i++) {
    nums[3 - i] = state % 31;
    state /= 31;
  }
}

bool IsPossible(int a, int b, int c, int d) {
  if (considered[a][b][c][d])
    return result[a][b][c][d];
  considered[a][b][c][d] = true;

  Circle cs[] = {{rocks[a], A}, {rocks[b], A}, {rocks[c], C}, {rocks[d], C}};
  vector<Point> cc1 = CrossPointCC(cs[0], cs[1]);
  vector<Point> cc2 = CrossPointCC(cs[2], cs[3]);
  if (cc1.empty())
    return false;
  if (cc2.empty())
    return false;

  vector<Point> rough_cand1(cc1);
  vector<Point> rough_cand2(cc2);
  for (int i = 0; i < 2; i++) {
    for (int j = 2; j < 4; j++) {
      Line l(cs[i].c, cs[j].c);
      for (int k = 0; k < 2; k++) {
        vector<Point> cl = CrossPointCL(cs[k], l);
        rough_cand1.insert(rough_cand1.end(), cl.begin(), cl.end());
        rough_cand2.insert(rough_cand2.end(), cl.begin(), cl.end());
      }

      for (int k = 2; k < 4; k++) {
        vector<Point> cl = CrossPointCL(cs[k], l);
        rough_cand2.insert(rough_cand2.end(), cl.begin(), cl.end());
      }
    }
  }

  for (int i = 0; i < 2; i++) {
    for (Point p2 : cc2) {
      Line l(cs[i].c, p2);
      for (int j = 0; j < 2; j++) {
        vector<Point> cl1 = CrossPointCL(cs[j], l);
        rough_cand1.insert(rough_cand1.end(), cl1.begin(), cl1.end());
      }
    }
  }

  for (int i = 2; i < 4; i++) {
    for (Point p1 : cc1) {
      Line l(cs[i].c, p1);
      for (int k = 2; k < 4; k++) {
        vector<Point> cl2 = CrossPointCL(cs[k], l);
        rough_cand2.insert(rough_cand2.end(), cl2.begin(), cl2.end());
      }
    }
  }

  vector<Point> cand1;
  vector<Point> cand2;

  for (Point p1 : rough_cand1) {
    if (abs(p1 - cs[0].c) >= cs[0].r + EPS)
      continue;
    if (abs(p1 - cs[1].c) >= cs[1].r + EPS)
      continue;
    cand1.push_back(p1);
  }

  for (Point p2 : rough_cand2) {
    if (abs(p2 - cs[2].c) >= cs[2].r + EPS)
      continue;
    if (abs(p2 - cs[3].c) >= cs[3].r + EPS)
      continue;
    cand2.push_back(p2);
  }

  sort(cand1.begin(), cand1.end());
  cand1.erase(unique(cand1.begin(), cand1.end()), cand1.end());
  sort(cand2.begin(), cand2.end());
  cand2.erase(unique(cand2.begin(), cand2.end()), cand2.end());
  for (Point p1 : cand1) {
    for (Point p2 : cand2) {
      if (abs(p1 - p2) < B + EPS)
        return result[a][b][c][d] = true;
    }
  }

  return result[a][b][c][d] = false;
}

int main() {
  scanf("%d", &n);
  scanf("%lf%lf%lf", &A, &B, &C);
  swap(A, B);
  for (int i = 0; i < n; i++) {
    double x, y;
    scanf("%lf%lf", &x, &y);
    rocks[i] = Point(x, y);
  }

  int ans = INF;
  queue<int> q;
  fill(mind, mind + 11451419, INF);
  for (int i = 0; i < 4; i++) {
    int nums[] = {1, 2, 3, 4};

    nums[i] = 0;
    NormalizeNum(nums);

    bool is_goal = false;
    int state = CalcState(nums, &is_goal);
    mind[state] = 0;
    q.push(state);
    if (is_goal) {
      puts("0");
      return 0;
    }
  }

  while (!q.empty()) {
    int state = q.front();
    q.pop();
    int nums[4];
    State2Num(state, nums);

    if (nums[0] == 0) {
      int b = nums[1] - 1;
      int c = nums[2] - 1;
      int d = nums[3] - 1;

      for (int a = 0; a < n; a++) {
        if (b == a || c == a || d == a)
          continue;

        if (IsPossible(a, b, c, d)) {
          // printf("%d %d %d %d\\n", a, b, c, d);
          for (int j = 1; j < 4; j++) {
            int nums2[4];

            memcpy(nums2, nums, sizeof(nums2));
            nums2[0] = a + 1;
            nums2[j] = 0;
            NormalizeNum(nums2);

            bool is_goal = false;
            int next_state = CalcState(nums2, &is_goal);
            if (is_goal) {
              printf("%d\\n", mind[state] + 1);
              return 0;
            }

            if (mind[next_state] > mind[state] + 1) {
              mind[next_state] = mind[state] + 1;
              q.push(next_state);
            }
          }
        }
      }
    } else {
      int a = nums[0] - 1;
      int b = nums[1] - 1;
      int d = nums[3] - 1;

      for (int c = 0; c < n; c++) {
        if (a == c || b == c || d == c)
          continue;

        if (IsPossible(a, b, c, d)) {
          // printf("%d %d %d %d\\n", a, b, c, d);
          for (int j = 0; j < 4; j++) {
            if (j == 2)
              continue;

            int nums2[4];

            memcpy(nums2, nums, sizeof(nums2));
            nums2[2] = c + 1;
            nums2[j] = 0;
            NormalizeNum(nums2);

            bool is_goal = false;
            int next_state = CalcState(nums2, &is_goal);
            if (is_goal) {
              printf("%d\\n", mind[state] + 1);
              return 0;
            }

            if (mind[next_state] > mind[state] + 1) {
              mind[next_state] = mind[state] + 1;
              q.push(next_state);
            }
          }
        }
      }
    }
  }

  puts("-1");
}

Error: TLE

Fixed submission:

#include <algorithm>
#include <cassert>
#include <complex>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;

#define EQ(x, y) (abs((x) - (y)) < EPS)
#define EPS 1e-8
#define INF 0x33433433

#define X real()
#define Y imag()

typedef pair<int, int> Pair;
typedef complex<double> Point;
typedef Point Vec;

namespace std {
bool operator<(Point a, Point b) {
  if (a.X != b.X)
    return a.X < b.X;
  return a.Y < b.Y;
}
} // namespace std

int n;
double A, B, C;
Point rocks[50];
int mind[11451419];

class Line : public vector<Point> {
public:
  Line() { resize(2); }
  Line(Point a, Point b) {
    push_back(a);
    push_back(b);
  }
};

struct Circle {
  Point c;
  double r;
};

double dot(Point a, Point b) { return (conj(a) * b).X; }

double cross(Point a, Point b) { return (conj(a) * b).Y; }

int ccw(Point a, Point b, Point c) {
  b -= a;
  c -= a;
  if (cross(b, c) > 0)
    return 1;
  if (cross(b, c) < 0)
    return -1;
  if (dot(b, c) < 0)
    return 2;
  if (norm(b) < norm(c))
    return -2;
  return 0;
}

Point proj(Line l, Point p) {
  Point v = l[1] - l[0];
  double ratio = dot(v, p - l[0]) / norm(v);
  return l[0] + ratio * v;
}

double DistLP(Line l, Point p) { return abs(p - proj(l, p)); }

vector<Point> CrossPointCL(Circle c, Line l) {
  vector<Point> ret;
  double d = DistLP(l, c.c);
  if (d < c.r + EPS) {
    double ratio = (d > c.r) ? 0.0 : sqrt(c.r * c.r - d * d);
    Point sgn = (l[0] - l[1]) / abs(l[0] - l[1]);
    ret.push_back(proj(l, c.c) + ratio * sgn);
    ret.push_back(proj(l, c.c) - ratio * sgn);
  }

  return ret;
}

vector<Point> CrossPointCC(Circle c1, Circle c2) {
  vector<Point> ret;
  double d = abs(c1.c - c2.c);
  double rc = (d * d + c1.r * c1.r - c2.r * c2.r) / (2 * d);
  double dfr = c1.r * c1.r - rc * rc;

  if (EQ(dfr, 0.0))
    dfr = 0.0;
  else if (dfr < 0.0)
    return ret;

  double rs = sqrt(dfr);
  Point sgn = (c2.c - c1.c) / d;
  ret.push_back(c1.c + sgn * Point(rc, rs));
  if (dfr > 0.0)
    ret.push_back(c1.c + sgn * Point(rc, -rs));
  return ret;
}

void NormalizeNum(int *nums) {
  if (nums[0] > nums[1])
    swap(nums[0], nums[1]);
  if (nums[2] > nums[3])
    swap(nums[2], nums[3]);
}

int CalcState(int *nums, bool *is_goal) {
  int state = 0;
  for (int i = 0; i < 4; i++) {
    state = state * 31 + nums[i];
    if (nums[i] == n && is_goal)
      *is_goal = true;
  }
  return state;
}

void State2Num(int state, int *nums) {
  for (int i = 0; i < 4; i++) {
    nums[3 - i] = state % 31;
    state /= 31;
  }
}

bool considered[30][30][30][30];
bool result[30][30][30][30];

bool IsPossible(int a, int b, int c, int d) {
  if (considered[a][b][c][d])
    return result[a][b][c][d];
  considered[a][b][c][d] = true;

  Circle cs[] = {{rocks[a], A}, {rocks[b], A}, {rocks[c], C}, {rocks[d], C}};
  vector<Point> cc = CrossPointCC(cs[0], cs[1]);
  vector<Point> cc2 = CrossPointCC(cs[2], cs[3]);
  if (cc.empty())
    return false;
  if (cc2.empty())
    return false;

  vector<Point> ps1(cc);
  vector<Point> ps2(cc2);

  cc.push_back(cs[0].c);
  cc.push_back(cs[1].c);
  cc2.push_back(cs[2].c);
  cc2.push_back(cs[3].c);
  for (Point p1 : cc) {
    for (Point p2 : cc2) {
      for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
          Line l(p1, p2);
          vector<Point> cl1 = CrossPointCL(cs[i], l);
          vector<Point> cl2 = CrossPointCL(cs[j], l);

          for (Point p : cl1) {
            if (abs(p - cs[0].c) < cs[0].r + EPS) {
              if (abs(p - cs[1].c) < cs[1].r + EPS) {
                ps1.push_back(p);
              }
            }
          }

          for (Point p : cl2) {
            if (abs(p - cs[2].c) < cs[2].r + EPS) {
              if (abs(p - cs[3].c) < cs[3].r + EPS) {
                ps2.push_back(p);
              }
            }
          }
        }
      }
    }
  }

  sort(ps1.begin(), ps1.end());
  ps1.erase(unique(ps1.begin(), ps1.end()), ps1.end());
  sort(ps2.begin(), ps2.end());
  ps2.erase(unique(ps2.begin(), ps2.end()), ps2.end());

  for (Point p1 : ps1) {
    for (Point p2 : ps2) {
      if (abs(p1 - p2) < B + EPS)
        return result[a][b][c][d] = true;
    }
  }

  return result[a][b][c][d] = false;
}

int main() {
  scanf("%d", &n);
  scanf("%lf%lf%lf", &A, &B, &C);
  swap(A, B);
  for (int i = 0; i < n; i++) {
    double x, y;
    scanf("%lf%lf", &x, &y);
    rocks[i] = Point(x, y);
  }

  int ans = INF;
  queue<int> q;
  fill(mind, mind + 11451419, INF);
  for (int i = 0; i < 4; i++) {
    int nums[] = {1, 2, 3, 4};

    nums[i] = 0;
    NormalizeNum(nums);

    bool is_goal = false;
    int state = CalcState(nums, &is_goal);
    mind[state] = 0;
    q.push(state);
    if (is_goal) {
      puts("0");
      return 0;
    }
  }

  while (!q.empty()) {
    int state = q.front();
    q.pop();
    int nums[4];
    State2Num(state, nums);

    if (nums[0] == 0) {
      int b = nums[1] - 1;
      int c = nums[2] - 1;
      int d = nums[3] - 1;

      for (int a = 0; a < n; a++) {
        if (b == a || c == a || d == a)
          continue;

        if (IsPossible(a, b, c, d)) {
          // printf("%d %d %d %d\\n", a, b, c, d);
          for (int j = 1; j < 4; j++) {
            int nums2[4];

            memcpy(nums2, nums, sizeof(nums2));
            nums2[0] = a + 1;
            nums2[j] = 0;
            NormalizeNum(nums2);

            bool is_goal = false;
            int next_state = CalcState(nums2, &is_goal);
            if (is_goal) {
              printf("%d\\n", mind[state] + 1);
              return 0;
            }

            if (mind[next_state] > mind[state] + 1) {
              mind[next_state] = mind[state] + 1;
              q.push(next_state);
            }
          }
        }
      }
    } else {
      int a = nums[0] - 1;
      int b = nums[1] - 1;
      int d = nums[3] - 1;

      for (int c = 0; c < n; c++) {
        if (a == c || b == c || d == c)
          continue;

        if (IsPossible(a, b, c, d)) {
          // printf("%d %d %d %d\\n", a, b, c, d);
          for (int j = 0; j < 4; j++) {
            if (j == 2)
              continue;

            int nums2[4];

            memcpy(nums2, nums, sizeof(nums2));
            nums2[2] = c + 1;
            nums2[j] = 0;
            NormalizeNum(nums2);

            bool is_goal = false;
            int next_state = CalcState(nums2, &is_goal);
            if (is_goal) {
              printf("%d\\n", mind[state] + 1);
              return 0;
            }

            if (mind[next_state] > mind[state] + 1) {
              mind[next_state] = mind[state] + 1;
              q.push(next_state);
            }
          }
        }
      }
    }
  }

  puts("-1");
}