人魚の魔女 (AOJ 2316)

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2316

問題

解法

やるだけ。鋭く凹んでる場合に今の軸の反対側の頂点が次の軸になることがあるので注意。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <math.h>
#include <assert.h>
#include <vector>

using namespace std;
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
static const double EPS = 1e-7;
static const double PI = acos(-1.0);

#define REP(i, n) for (int i = 0; i < (int)(n); i++)
#define FOR(i, s, n) for (int i = (s); i < (int)(n); i++)
#define FOREQ(i, s, n) for (int i = (s); i <= (int)(n); i++)
#define FORIT(it, c) for (__typeof((c).begin())it = (c).begin(); it != (c).end(); it++)
#define MEMSET(v, h) memset((v), h, sizeof(v))


#include <complex>

typedef complex<double> Point;

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

struct Circle {
  Point p;
  double r;
  Circle() {;}
  Circle(Point p, double r) : p(p), r(r) {;}
};

namespace std {
  bool operator<(const Point &lhs, const Point &rhs) {
    return lhs.real() == rhs.real() ? lhs.imag() < rhs.imag() : lhs.real() < rhs.real();
  }
}

//---------------------

int zoom = 1;

void chcolor(int r, int g, int b) {
    fprintf(stderr, "c.strokeStyle = 'rgb(%d, %d, %d)';\n", r, g, b);
}

void drawline(Line l) {
    fprintf(stderr, "line(%d, %d, %d, %d)\n",
            (int)(zoom*l[0].real()), 1980-(int)(zoom*l[0].imag()),
            (int)(zoom*l[1].real()), 1980-(int)(zoom*l[1].imag()));
}

//-----------------------------

inline double cross(const Point &a, const Point &b) {
  return imag(conj(a) * b);
}

inline double dot(const Point &a, const Point &b) {
  return real(conj(a) * b);
}

Point projection(const Line &l, const Point &p) {
  double t = dot(p - l[0], l[0] - l[1]) / norm(l[0] - l[1]);
  return l[0] + t * (l[0] - l[1]);
}

bool intersectSP(const Line &s, const Point &p) {
  return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < EPS;
}

vector<Point> crosspointLC(const Line &l, const Circle &c) {
  vector<Point> ret;
  Point center = projection(l, c.p);
  double d = abs(center - c.p);
  double t = sqrt(c.r * c.r - d * d);
  if (isnan(t)) { return ret; }
  Point vect = (l[1] - l[0]);
  vect /= abs(vect);
  ret.push_back(center - vect * t);
  if (t > EPS) {
    ret.push_back(center + vect * t);
  }
  return ret;
}

vector<Point> crosspointSC(const Line &s, const Circle &c) {
  vector<Point> ret;
  vector<Point> nret = crosspointLC(s, c);
  for (int i = 0; i < (int)nret.size(); i++) {
    if (intersectSP(s, nret[i])) { ret.push_back(nret[i]); }
  }
  return ret;
}

//==========================

const double sq2 = sqrt(2.0);
struct Square {
  Point axis;
  Point vect;
  int color;
  void Rotate(Point p) {
    vect = axis - p;
    axis = p;
    color++;
    assert(abs(abs(vect) - 1.0) <= EPS);
  }
  void SpecialRotate(Point p) {
    vect = ((axis - p) / sq2) * Point(cos(PI / 4.0), sin(PI / 4.0));
    axis = p;
    color += 2;
    assert(abs(abs(vect) - 1.0) <= EPS);
  }
};

int n, sx, gx;
Square square;
Line segments[100010];

inline bool CompareRadian(double lhsR, bool lhsS, double rhsR, bool rhsS) {
  if (lhsS) { lhsR += PI / 4.0; }
  if (rhsS) { rhsR += PI / 4.0; }
  return lhsR - rhsR < -EPS;
}

pair<Point, double> FirstPoint(const vector<Point> &candidate, int index) {
  double minRadian = 1e+100;
  bool minSecond = false;
  Point firstPoint(-1e+100, -1e+100);
  FORIT(it, candidate) {
    if (it->real() < square.axis.real()) { continue; }
    Point v = *it - square.axis;
    bool second = abs(v) - 1.0 < EPS ? 0 : 1;
    v /= abs(v);
    double radian = acos(dot(square.vect, v));
    if (isnan(radian)) { radian = PI; }
    //cout << *it << " " << v << " " << radian << endl;
    if (CompareRadian(radian, second, minRadian, minSecond)) {
      minSecond = second;
      minRadian = radian;
      firstPoint = *it;
    }
  }
  assert(minRadian != 1e+100);
  return make_pair(firstPoint, minRadian);
}

int main(int argc, char *argv[]) {
  if (argc >= 2) {
    zoom = atoi(argv[1]);
  }
  while (scanf("%d %d %d", &n, &sx, &gx) > 0) {
    {
      // input
      int px = 0;
      int py = 0;
      int v = scanf("%d %d", &px, &py);
      assert(v == 2);
      REP(i, n - 1) {
        int nx, ny;
        int v = scanf("%d %d", &nx, &ny);
        assert(v == 2);
        segments[i] = Line(Point(px, py), Point(nx, ny));
        px = nx;
        py = ny;
      }
      segments[n - 1] = Line(segments[n - 2][1], segments[n - 2][1] * 2.0 - segments[n - 2][0]);
      segments[n] = Line(segments[n - 1][1], segments[n - 1][1] * 2.0 - segments[n - 1][0]);
    }
    
    // init
    square.vect = segments[0][0] - segments[0][1];
    square.vect /= abs(square.vect);
    square.axis = -square.vect * (1.0 + (sx - segments[0][0].real()) / -square.vect.real()) + segments[0][0];
    square.color = 0;
    int index = 0;

#ifdef visualize
    chcolor(0, 0, 0);
    REP(i, n - 1) {
        drawline(segments[i]);
    }
#endif

    // process
    while (true) {

#ifdef visualize
      {
        int co = square.color % 4;
        if (co == 0) { chcolor(250, 10, 10); }
        else if (co == 1) { chcolor(0, 180, 0); }
        else if (co == 2) { chcolor(0, 0, 200); }
        else if (co == 3) { chcolor(100, 100, 100); }

        drawline(Line(square.axis, square.axis + square.vect));
        drawline(Line(square.axis, square.axis + square.vect * Point(0, -1)));
        drawline(Line(square.axis + square.vect * Point(1, -1), square.axis + square.vect));
        drawline(Line(square.axis + square.vect * Point(1, -1), square.axis + square.vect * Point(0, -1)));
      }
#endif
      if (square.axis.real() - gx >= -EPS) { break; }

      Circle c1(square.axis, 1.0);
      Circle c2(square.axis, sq2);;
      vector<Point> candidate[5];
      candidate[0] = crosspointSC(segments[index], c1);
      candidate[1] = crosspointSC(segments[index + 1], c1);
      candidate[2] = crosspointSC(segments[index + 2], c1);
      candidate[3] = crosspointSC(segments[index + 1], c2);
      candidate[4] = crosspointSC(segments[index + 2], c2);
      vector<Point> candidates;
      REP(i, 5) FORIT(it, candidate[i]) {
        candidates.push_back(*it);
      }
      pair<Point, double> rot = FirstPoint(candidates, index);
     //cout << square.vect << endl;
     //cout << rot.first << " " << rot.second << " " << abs(rot.first - square.axis) << endl;
      if (abs(rot.first - square.axis) - 1.0 < EPS) {
        square.Rotate(rot.first);
      } else {
        square.SpecialRotate(rot.first);
      }
      if (!intersectSP(segments[index], rot.first)) { index++; }
    }
    const char *ans[4] = { "Red", "Green", "Blue", "White" };
    printf("%s\n", ans[square.color % 4]);
    //cout << square.color << endl;;
  }
}