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

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <set>
#include <vector>

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

using namespace std;

const double EPS = 1e-8;
const double PI = acos(-1);

struct point {
  double x, y;
  point() : x(0), y(0) {}
  point(double x, double y) : x(x), y(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); }
  bool operator==(const point &a) const {
    return abs(x - a.x) < EPS && abs(y - a.y) < EPS;
  }
  bool operator!=(const point &a) const {
    return abs(x - a.x) > EPS || abs(y - a.y) > EPS;
  }
};

point operator*(double c, const point &a) { return point(c * a.x, c * a.y); }

inline double dot(const point &a, const point &b) {
  return a.x * b.x + a.y * b.y;
}

inline double cross(const point &a, const point &b) {
  return a.x * b.y - a.y * b.x;
}

inline point rot(const point &a, double theta) {
  return point(a.x * cos(theta) - a.y * sin(theta),
               a.x * sin(theta) + a.y * cos(theta));
}

inline double arg(const point &a) {
  double t = atan2(a.y, a.x);
  return t < 0 ? t + 2 * PI : t;
}

struct segment {
  point a, b;
  segment() {}
  segment(const point &a, const point &b) : a(a), b(b) {}
};

typedef vector<point> polygon;

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

inline bool intersect(const segment &S1, const segment &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;
}

inline bool cover(const segment &S, const point &p) {
  return abs(cross(S.a - p, S.b - p)) < EPS && dot(S.a - p, S.b - p) < EPS;
}

inline point get_intersect(const segment &S1, const segment &S2) {
  double a1 = cross(S1.b - S1.a, S2.b - S2.a);
  double a2 = cross(S1.b - S1.a, S1.b - S2.a);
  if (abs(a1) < EPS) {
    if (cover(S1, S2.a))
      return S2.a;
    if (cover(S1, S2.b))
      return S2.b;
    if (cover(S2, S1.a))
      return S1.a;
    return S1.b;
  }
  return S2.a + a2 / a1 * (S2.b - S2.a);
}

bool is_perm(vector<int> a) {
  sort(a.begin(), a.end());
  return unique(a.begin(), a.end()) - a.begin() == a.size();
}

vector<int> simulate(point p, const polygon &F, double theta) {
  int n = F.size();
  vector<int> res;
  bool used[8] = {};
  while (res.size() < n) {
    point v(cos(theta), sin(theta));
    segment S(p, p + 200 * v);

    int i0 = -1;
    segment e; // 反射する辺
    point q;   // 反射する点
    rep(i, n) {
      e = segment(F[i], F[(i + 1) % n]);
      if (intersect(S, e)) {
        q = get_intersect(S, e);
        if (q != p) {
          i0 = i;
          break;
        }
      }
    }

    assert(i0 != -1);
    if (used[i0])
      return res; // 同じ辺に二回反射したらダメ
    used[i0] = true;

    if (q == e.a || q == e.b)
      return res; // 角に当たったらダメ

    res.push_back(i0);

    // 反射角を求める
    double phi = arg(e.b - e.a);
    point dir = rot(q - p, -phi);
    dir.y *= -1;
    dir = rot(dir, phi);
    theta = arg(dir);

    p = q;
  }

  return res;
}

void dfs(point s, int cnt, int used, polygon G, vector<double> &theta) {
  int n = G.size();

  if (cnt == n - 1) {
    int i;
    for (i = 0; used & (1 << i); i++)
      ;
    theta.push_back(arg(G[i] - s));
    theta.push_back(arg(G[(i + 1) % n] - s));
    return;
  }

  rep(i, n) if (!(used & (1 << i))) {
    theta.push_back(arg(G[i] - s));
    theta.push_back(arg(G[(i + 1) % n] - s));

    segment e(G[i], G[(i + 1) % n]);
    polygon H(n);
    rep(j, n) {
      point p = G[j];
      // p を e に対称な位置に移す
      double phi = arg(e.b - e.a);
      point tmp = rot(p - e.a, -phi);
      tmp.y *= -1;
      H[j] = rot(tmp, phi) + e.a;
    }
    dfs(s, cnt + 1, used | (1 << i), H, theta);
  }
}

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

int main() {
  for (int n; scanf("%d", &n), n;) {
    point s;
    scanf("%lf%lf", &s.x, &s.y);
    polygon F(n);
    rep(i, n) scanf("%lf%lf", &F[i].x, &F[i].y);

    vector<double> theta;
    dfs(s, 0, 0, F, theta);
    sort(theta.begin(), theta.end());
    theta.erase(unique(theta.begin(), theta.end(), eq), theta.end());
    theta.push_back(theta[0] + 2 * PI);

    vector<double> phi;
    rep(i, (int)theta.size() - 1) phi.push_back((theta[i] + theta[i + 1]) / 2);

    set<vector<int>> ans;
    // TLE が厳しいので 1 つ置きにシミュレーション. 2
    // つが同じ結果ならスキップしたものも同じ結果
    vector<int> prev;
    static bool skip[200000];
    rep(i, phi.size()) skip[i] = false;
    rep(i, phi.size() / 2) {
      vector<int> hit = simulate(s, F, phi[2 * i]);
      if (hit.size() == n && is_perm(hit)) {
        ans.insert(hit);
        if (hit == prev)
          skip[2 * i - 1] = true;
      }
      prev = hit;
    }
    rep(i, phi.size() / 2 + 1) {
      if (2 * i + 1 < phi.size() && !skip[2 * i + 1]) {
        vector<int> hit = simulate(s, F, phi[2 * i + 1]);
        if (hit.size() == n && is_perm(hit)) {
          ans.insert(hit);
        }
      }
    }

    printf("%d\\n", ans.size());
  }

  return 0;
}

Error: TLE

Fixed submission:

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <set>
#include <vector>

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

using namespace std;

const double EPS = 1e-8;
const double PI = acos(-1);

struct point {
  double x, y;
  point() : x(0), y(0) {}
  point(double x, double y) : x(x), y(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); }
  bool operator==(const point &a) const {
    return abs(x - a.x) < EPS && abs(y - a.y) < EPS;
  }
  bool operator!=(const point &a) const {
    return abs(x - a.x) > EPS || abs(y - a.y) > EPS;
  }
};

point operator*(double c, const point &a) { return point(c * a.x, c * a.y); }

inline double dot(const point &a, const point &b) {
  return a.x * b.x + a.y * b.y;
}

inline double cross(const point &a, const point &b) {
  return a.x * b.y - a.y * b.x;
}

inline point rot(const point &a, double theta) {
  return point(a.x * cos(theta) - a.y * sin(theta),
               a.x * sin(theta) + a.y * cos(theta));
}

inline double arg(const point &a) {
  double t = atan2(a.y, a.x);
  return t < 0 ? t + 2 * PI : t;
}

struct segment {
  point a, b;
  segment() {}
  segment(const point &a, const point &b) : a(a), b(b) {}
};

typedef vector<point> polygon;

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

inline bool intersect(const segment &S1, const segment &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;
}

inline bool cover(const segment &S, const point &p) {
  return abs(cross(S.a - p, S.b - p)) < EPS && dot(S.a - p, S.b - p) < EPS;
}

inline point get_intersect(const segment &S1, const segment &S2) {
  double a1 = cross(S1.b - S1.a, S2.b - S2.a);
  double a2 = cross(S1.b - S1.a, S1.b - S2.a);
  if (abs(a1) < EPS) {
    if (cover(S1, S2.a))
      return S2.a;
    if (cover(S1, S2.b))
      return S2.b;
    if (cover(S2, S1.a))
      return S1.a;
    return S1.b;
  }
  return S2.a + a2 / a1 * (S2.b - S2.a);
}

bool is_perm(vector<int> a) {
  sort(a.begin(), a.end());
  return unique(a.begin(), a.end()) - a.begin() == a.size();
}

vector<int> simulate(point p, const polygon &F, double theta) {
  int n = F.size();
  vector<int> res;
  bool used[8] = {};
  while (res.size() < n) {
    point v(cos(theta), sin(theta));
    segment S(p, p + 200 * v);

    int i0 = -1;
    segment e; // 反射する辺
    point q;   // 反射する点
    rep(i, n) {
      e = segment(F[i], F[(i + 1) % n]);
      if (intersect(S, e)) {
        q = get_intersect(S, e);
        if (q != p) {
          i0 = i;
          break;
        }
      }
    }

    assert(i0 != -1);
    if (used[i0])
      return res; // 同じ辺に二回反射したらダメ
    used[i0] = true;

    if (q == e.a || q == e.b)
      return res; // 角に当たったらダメ

    res.push_back(i0);

    // 反射角を求める
    double phi = arg(e.b - e.a);
    point dir = rot(q - p, -phi);
    dir.y *= -1;
    dir = rot(dir, phi);
    theta = arg(dir);

    p = q;
  }

  return res;
}

void dfs(point s, int cnt, int used, polygon G, vector<double> &theta) {
  int n = G.size();

  if (cnt == n - 1) {
    int i;
    for (i = 0; used & (1 << i); i++)
      ;
    theta.push_back(arg(G[i] - s));
    theta.push_back(arg(G[(i + 1) % n] - s));
    return;
  }

  rep(i, n) if (!(used & (1 << i))) {
    theta.push_back(arg(G[i] - s));
    theta.push_back(arg(G[(i + 1) % n] - s));

    segment e(G[i], G[(i + 1) % n]);
    polygon H(n);
    rep(j, n) {
      point p = G[j];
      // p を e に対称な位置に移す
      double phi = arg(e.b - e.a);
      point tmp = rot(p - e.a, -phi);
      tmp.y *= -1;
      H[j] = rot(tmp, phi) + e.a;
    }
    dfs(s, cnt + 1, used | (1 << i), H, theta);
  }
}

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

int main() {
  for (int n; scanf("%d", &n), n;) {
    point s;
    scanf("%lf%lf", &s.x, &s.y);
    polygon F(n);
    rep(i, n) scanf("%lf%lf", &F[i].x, &F[i].y);

    vector<double> theta;
    dfs(s, 0, 0, F, theta);
    sort(theta.begin(), theta.end());
    theta.erase(unique(theta.begin(), theta.end(), eq), theta.end());
    theta.push_back(theta[0] + 2 * PI);

    vector<double> phi;
    rep(i, (int)theta.size() - 1) phi.push_back((theta[i] + theta[i + 1]) / 2);

    set<vector<int>> ans;
    // TLE が厳しいので 1 つ置きにシミュレーション. 2
    // つが同じ結果ならスキップしたものも同じ結果
    vector<int> prev;
    static bool skip[200000];
    rep(i, phi.size()) skip[i] = false;
    rep(i, phi.size() / 2) {
      vector<int> hit = simulate(s, F, phi[2 * i]);
      if (hit.size() == n && is_perm(hit)) {
        ans.insert(hit);
        if (hit == prev && phi[2 * i] - phi[2 * i - 2] < 1e-5)
          skip[2 * i - 1] = true;
      }
      prev = hit;
    }
    rep(i, phi.size() / 2 + 1) {
      if (2 * i + 1 < phi.size() && !skip[2 * i + 1]) {
        vector<int> hit = simulate(s, F, phi[2 * i + 1]);
        if (hit.size() == n && is_perm(hit)) {
          ans.insert(hit);
        }
      }
    }

    printf("%d\\n", ans.size());
  }

  return 0;
}