Submission #220598

#TimeUsernameProblemLanguageResultExecution timeMemory
220598rama_pangFences (JOI18_fences)C++14
100 / 100
30 ms4100 KiB
#include <bits/stdc++.h>
using namespace std;
using type_t = double;
const type_t EPS = 1e-7;

struct Point {
  type_t x, y;
  Point() {}
  Point(type_t a, type_t b) : x(a), y(b) {}

  Point operator + (const Point &o) const {
    return Point(x + o.x, y + o.y);
  }

  Point operator - (const Point &o) const {
    return Point(x - o.x, y - o.y);
  }

  friend ostream& operator << (ostream &stream, const Point &p) {
    stream << "(" << p.x << ", " << p.y << ")";
  }
};

struct Line {
  Point a, b;
  Line() {}
  Line(Point a, Point b) : a(a), b(b) {}

  friend ostream& operator << (ostream &stream, const Line &l) {
    stream << "(" << l.a << ", " << l.b << ")";
  }
};

bool Clockwise(Point a, Point b, Point c) { // cross product of vector ab and ac
  Point v1 = b - a, v2 = c - a;
  return (v1.x * v2.y - v1.y * v2.x) > 0;
}

bool CounterClockwise(Point a, Point b, Point c) { // cross product of vector ab and ac
  Point v1 = b - a, v2 = c - a;
  return (v1.x * v2.y - v1.y * v2.x) < 0;
}

bool IntersectLine(Line a, Line b, bool IsTouchIntersect) {
  bool o1 = false, o2 = false;
  o1 |= Clockwise(a.a, a.b, b.a) && CounterClockwise(a.a, a.b, b.b);
  o1 |= CounterClockwise(a.a, a.b, b.a) && Clockwise(a.a, a.b, b.b);
  
  o2 |= Clockwise(b.a, b.b, a.a) && CounterClockwise(b.a, b.b, a.b);
  o2 |= CounterClockwise(b.a, b.b, a.a) && Clockwise(b.a, b.b, a.b);
  
  if (IsTouchIntersect) {
    o1 |= Clockwise(a.a, a.b, b.a) && !Clockwise(a.a, a.b, b.b);
    o1 |= !Clockwise(a.a, a.b, b.a) && Clockwise(a.a, a.b, b.b);
    o2 |= Clockwise(b.a, b.b, a.a) && !Clockwise(b.a, b.b, a.b);
    o2 |= !Clockwise(b.a, b.b, a.a) && Clockwise(b.a, b.b, a.b);
  }

  return o1 && o2;
}

Line PointPointDistance(Point p, Point q) {
  return Line(p, q);
}

Line PointLineDistance(Point p, Line l) {
  Point a = l.a, b = l.b;
  Point vl = b - a;
  Point vp = p - a;

  type_t dot = (vl.x * vp.x + vl.y * vp.y);
  type_t sqr = vl.x * vl.x + vl.y * vl.y;
  type_t partial = dot / sqr;
  
  if (partial > 1 || partial < 0 || sqr == 0) {
    Point d1 = p - l.a;
    Point d2 = p - l.b;
    if (d1.x * d1.x + d1.y * d1.y < d2.x * d2.x + d2.y * d2.y) {
      return PointPointDistance(l.a, p);
    } else {
      return PointPointDistance(l.b, p);
    }
  }

  vl.x *= partial;
  vl.y *= partial;

  return PointPointDistance(vl + a, p);
}

int N, S;
vector<Line> L;

const Line HALFLINE = Line(Point(0, 0), Point(1, 400));

bool IntersectSquare(Line line) {
  bool res = false;
  res |= IntersectLine(line, Line(Point(S, S), Point(-S, S)), false); // top side
  res |= IntersectLine(line, Line(Point(S, S), Point(S, -S)), false); // right side
  res |= IntersectLine(line, Line(Point(-S, -S), Point(-S, S)), false); // left side
  res |= IntersectLine(line, Line(Point(-S, -S), Point(S, -S)), false); // bottom side
  res |= IntersectLine(line, Line(Point(-S, S), Point(S, -S)), false); // diagonal 1
  res |= IntersectLine(line, Line(Point(S, S), Point(-S, -S)), false); // diagonal 2
  return res;
}

void Read() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);

  cin >> N >> S;

  for (int i = 0; i < N; i++) {
    int A, B, C, D;
    cin >> A >> B >> C >> D;
    L.emplace_back(Point(A, B), Point(C, D));
  }

  // Square Corners
  L.emplace_back(Point(S, S), Point(S, S));
  L.emplace_back(Point(S, -S), Point(S, -S));
  L.emplace_back(Point(-S, -S), Point(-S, -S));
  L.emplace_back(Point(-S, S), Point(-S, S)); 
}

struct Edge {
  int u, v;
  Line l;
  bool CrossLine;
  type_t Distance;
  Edge() {}
  Edge(int u, int v, Line l) : u(u), v(v), l(l) {}
};

vector<Edge> edges;

void AddEdge(int u, int v) {
  if (!IntersectSquare(PointPointDistance(L[u].a, L[v].a))) {
    edges.emplace_back(u, v, PointPointDistance(L[u].a, L[v].a));
  }
  if (!IntersectSquare(PointPointDistance(L[u].a, L[v].b))) {
    edges.emplace_back(u, v, PointPointDistance(L[u].a, L[v].b));
  }
  if (!IntersectSquare(PointPointDistance(L[u].b, L[v].a))) {
    edges.emplace_back(u, v, PointPointDistance(L[u].b, L[v].a));
  }
  if (!IntersectSquare(PointPointDistance(L[u].b, L[v].b))) {
    edges.emplace_back(u, v, PointPointDistance(L[u].b, L[v].b));
  }

  if (!IntersectSquare(PointLineDistance(L[u].a, L[v]))) {
    edges.emplace_back(u, v, PointLineDistance(L[u].a, L[v]));
  }
  if (!IntersectSquare(PointLineDistance(L[u].b, L[v]))) {
    edges.emplace_back(u, v, PointLineDistance(L[u].b, L[v]));
  }
  if (!IntersectSquare(PointLineDistance(L[v].a, L[u]))) {
    edges.emplace_back(u, v, PointLineDistance(L[v].a, L[u]));
  }
  if (!IntersectSquare(PointLineDistance(L[v].b, L[u]))) {
    edges.emplace_back(u, v, PointLineDistance(L[v].b, L[u]));
  }
} 

void BuildGraph() {
  int n = L.size();

  { // Split segments that intersecti with HALFLINE into 2 so there are no segments that intersect with HALFLINE
    vector<Line> newL;
    for (int i = 0; i < n; i++) {
      if (IntersectLine(L[i], HALFLINE, true)) { 
        Point Vector = L[i].b - L[i].a;
        type_t l = 0, r = 1;
        for (int it = 0; it < 30; it++) {
          type_t m = (l + r) / 2;
          Point mp = L[i].a + Point(Vector.x * m, Vector.y * m);
          Line p = Line(L[i].a, mp);
          if (IntersectLine(p, HALFLINE, true)) {
            r = m;
          } else {
            l = m;
          }
        }
        Point m;
        m = L[i].a + Point(Vector.x * (r - EPS), Vector.y * (r - EPS));
        newL.emplace_back(L[i].a, m);
        m = L[i].a + Point(Vector.x * (r + EPS), Vector.y * (r + EPS));
        newL.emplace_back(m, L[i].b);
      } else {
        newL.emplace_back(L[i]);
      }
    }
    L = newL;
  }

  n = L.size();

  for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
      AddEdge(i, j);
    }
  }

  for (auto &e : edges) {
    e.Distance = sqrt((e.l.a.x - e.l.b.x) * (e.l.a.x - e.l.b.x) + (e.l.a.y - e.l.b.y) * (e.l.a.y - e.l.b.y));
    e.CrossLine = IntersectLine(e.l, HALFLINE, true);
  }
}

type_t Solve() {
  int n = L.size();
  vector<vector<type_t>> dist(n * 2, vector<type_t>(n * 2, 1e9));

  for (auto &e : edges) { // +n to mark if we have crossed HALFLINE an odd or even number of times
    if (e.CrossLine) {
      dist[e.u][e.v + n] = min(dist[e.u][e.v + n], e.Distance);
      dist[e.u + n][e.v] = min(dist[e.u + n][e.v], e.Distance);
      dist[e.v][e.u + n] = min(dist[e.v][e.u + n], e.Distance);
      dist[e.v + n][e.u] = min(dist[e.v + n][e.u], e.Distance);
    } else {
      dist[e.u][e.v] = min(dist[e.u][e.v], e.Distance);
      dist[e.u + n][e.v + n] = min(dist[e.u + n][e.v + n], e.Distance);
      dist[e.v][e.u] = min(dist[e.v][e.u], e.Distance);
      dist[e.v + n][e.u + n] = min(dist[e.v + n][e.u + n], e.Distance);
    } 
  }
  
  // Floyd-Warshall
  for (int k = 0; k < 2 * n; k++) {
    for (int i = 0; i < 2 * n; i++) {
      for (int j = 0; j < 2 * n; j++) {
        dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
      }
    }
  }

  type_t cycle = 1e9;
  for (int i = 0; i < n; i++) {
    cycle = min(cycle, dist[i][i + n]);
  }

  return cycle;
}

int main() {
  Read();
  BuildGraph();
  cout << fixed << setprecision(10) << Solve() << "\n";
  return 0;
}

Compilation message (stderr)

fences.cpp: In function 'std::ostream& operator<<(std::ostream&, const Point&)':
fences.cpp:21:3: warning: no return statement in function returning non-void [-Wreturn-type]
   }
   ^
fences.cpp: In function 'std::ostream& operator<<(std::ostream&, const Line&)':
fences.cpp:31:3: warning: no return statement in function returning non-void [-Wreturn-type]
   }
   ^
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...