// Hotter Colder (冷热游戏)
// PC/UVa IDs: 111404/10084, Popularity: C, Success rate: low Level: 3
// Verdict: Accepted
// Submission Date: 2011-11-09
// UVa Run Time: 0.008s
//
// 版权所有(C)2011,邱秋。metaphysis # yeah dot net
//
// [解题方法]
// 本题的实质是半平面与凸多边形求交问题,表示半平面的直线即相邻两个位置连线的垂直平分线。由于初始
// 区域为凸多边形,每次切割后仍为凸多边形。
#include <iostream>
#include <iomanip>
#include <cmath>
#include <queue>
using namespace std;
#define MAXPOLY 60
#define EPSILON (1E-7)
#define SAME_LINE 0
#define PARALLE_LINE 1
#define LINE_NO_INTERSECT 2
#define LINE_INTERSECT 3
#define LIE_ON_LINE 0
#define BELOW_LINE (-1)
#define ABOVE_LINE 1
struct point
{
double x;
double y;
};
struct polygon
{
int n;
point p[MAXPOLY];
};
struct line
{
double a, b, c;
};
struct segment
{
point p1, p2;
};
polygon region;
double lastX = 0.0, lastY = 0.0, lastArea = 100.0;
bool isLineOrEmpty;
// 判断两条直线是否平行。
bool paralleLine(line l1, line l2)
{
return ((fabs(l1.a - l2.a) <= EPSILON) && (fabs(l1.b - l2.b) <= EPSILON));
}
// 判断两条直线是否重合。
bool sameLine(line l1, line l2)
{
return (paralleLine(l1, l2) && (fabs(l1.c - l2.c) <= EPSILON));
}
// 点是否在线段的包围盒内。
bool point_in_box(point p, point b1, point b2)
{
return ((p.x >= min(b1.x, b2.x)) && (p.x <= max(b1.x, b2.x))
&& (p.y >= min(b1.y, b2.y)) && (p.y <= max(b1.y, b2.y)));
}
// 求两直线的交点。
int intersection_point(line l1, line l2, segment s, point &p)
{
if (sameLine(l1, l2))
return SAME_LINE;
if (paralleLine(l1, l2))
return PARALLE_LINE;
p.x = (l2.b * l1.c - l1.b * l2.c) / (l2.a * l1.b - l1.a * l2.b);
if (fabs(l1.b) > EPSILON)
p.y = -(l1.a * p.x + l1.c) / l1.b;
else
p.y = -(l2.a * p.x + l2.c) / l2.b;
if (point_in_box(p, s.p1, s.p2))
return LINE_INTERSECT;
else
return LINE_NO_INTERSECT;
}
// 通过两点求直线方程。
void points_to_line(point p1, point p2, line &l)
{
if (p1.x == p2.x)
{
l.a = 1;
l.b = 0;
l.c = -p1.x;
}
else
{
l.b = 1;
l.a = -(p1.y - p2.y) / (p1.x - p2.x);
l.c = -(l.a * p1.x) - (l.b * p1.y);
}
}
// 使用斜率和一点求直线方程。
void point_and_slope_to_line(point p, double m, line &l)
{
l.a = -m;
l.b = 1;
l.c = -((l.a * p.x) + (l.b * p.y));
}
// 利用有向面积计算多边形的面积,注意最后结果取绝对值,因为顶点顺序可能并不是按逆时针方向给出。
double calArea(point vertex[], int vertexNumber)
{
double total = 0.0;
for (int i = 0; i < vertexNumber; i++)
{
int j = (i + 1) % vertexNumber;
total += (vertex[i].x * vertex[j].y - vertex[j].x * vertex[i].y);
}
return fabs(total / 2.0);
}
// 判断点和直线的关系。
int relationPointLine(line l, point p)
{
if (fabs(l.a * p.x + l.b * p.y + l.c) <= EPSILON)
return LIE_ON_LINE;
if ((l.a * p.x + l.b * p.y + l.c) > 0)
return ABOVE_LINE;
else
return BELOW_LINE;
}
// 求半平面和凸多边形交的面积。
double area(double x, double y, string status)
{
// 若坐标未变化,则返回最后计算所得到的面积。
if (x == lastX && y == lastY)
return lastArea;
// 当可能区域为一条线段或者不可能存在这样的区域时,返回 0 值。
if (isLineOrEmpty)
return 0.0;
// 若给出的是 Same,且前后位置坐标不同,则物品在线段的垂直平分线上。
if (status == "Same")
{
isLineOrEmpty = true;
return 0.0;
}
// 求端点为(x,y),(lastX,lastY)的线段的垂直平分线直线方程。
line l1, l2;
point middle;
middle.x = (lastX + x) / 2.0;
middle.y = (lastY + y) / 2.0;
if (lastY == y)
{
l1.a = 1;
l1.b = 0;
l1.c = -middle.x;
}
else
{
double slope = -(lastX - x) / (lastY - y);
point_and_slope_to_line(middle, slope, l1);
}
// 判断是那个半平面与凸多边形相交。可以通过起点和垂直平分线的关系以及提示语来判断。若
// upPlane 为真,表示 a * x + b * y + c > 0 的半平面与凸多边形相交,反之则是另外
// 一个半平面与之相交。
bool upPlane = ((l1.a * x + l1.b * y + l1.c) > 0);
if (status == "Colder")
upPlane = !upPlane;
// 判断直线与多边形的相交情况,总是顺时针枚举凸包的顶点,以便生成的凸多边形顶点顺序仍然
// 是顺时针方向。
queue < point > up, down;
for (int i = 0; i < region.n; i++)
{
int j = (i + 1) % region.n;
// 直线和点的位置关系。
int ri = relationPointLine(l1, region.p[i]);
int rj = relationPointLine(l1, region.p[j]);
// 凸多边形两个相邻顶点在直线的异侧,表明直线和该线段有交点,求交点。
if (ri * rj < 0)
{
points_to_line(region.p[i], region.p[j], l2);
point intersect;
segment s = (segment) { region.p[i], region.p[j] };
intersection_point(l1, l2, s, intersect);
if (ri < 0)
{
down.push(region.p[i]);
down.push(intersect);
up.push(intersect);
}
else
{
up.push(region.p[i]);
up.push(intersect);
down.push(intersect);
}
}
// 凸多边形两个顶点或单个顶点在直线上。
else if (ri * rj == 0)
{
if (ri == 0 && rj == 0)
{
up.push(region.p[i]);
down.push(region.p[i]);
}
else if (ri == 0 && rj < 0)
{
up.push(region.p[i]);
down.push(region.p[i]);
}
else if (ri == 0 && rj > 0)
{
down.push(region.p[i]);
up.push(region.p[i]);
}
else if (ri < 0 && rj == 0)
down.push(region.p[i]);
else if (ri > 0 && rj == 0)
up.push(region.p[i]);
}
// 凸多边形的两个顶点在直线的同侧。
else
{
if (ri > 0)
up.push(region.p[i]);
else
down.push(region.p[i]);
}
}
// 根据所在半平面取相应的顶点。
if (upPlane)
{
region.n = 0;
while (!up.empty())
{
region.p[(region.n)++] = up.front();
up.pop();
}
}
else
{
region.n = 0;
while (!down.empty())
{
region.p[(region.n)++] = down.front();
down.pop();
}
}
// 计算面积。
return calArea(region.p, region.n);
}
int main (int argc, char const* argv[])
{
double x, y;
string status;
cout.precision(2);
cout.setf(ios::fixed | ios::showpoint);
// 初始凸多边形即为给定的正方形范围顶点。
region.n = 4;
region.p[0] = (point) { 0.0, 0.0 };
region.p[1] = (point) { 0.0, 10.0 };
region.p[2] = (point) { 10.0, 10.0 };
region.p[3] = (point) { 10.0, 0.0 };
lastX = lastY = 0.0;
isLineOrEmpty = false;
// 读入位置和结果。
while (cin >> x >> y >> status)
{
cout << area(x, y, status) << endl;
lastX = x;
lastY = y;
}
return 0;
}