kuangbin2

半平面交

求半平面交步骤:

将所有边按角度排序
利用双端队列存储直线,正向遍历所有边
检查队尾直线和队尾前一条的交点是否在当前边的右边,如果在右边,则将队尾元素踢出队列
对于队头重复3
最后队列中的所有直线就是构成最终半平面交的边集
相邻两边的交点就是最终半平面交的所有顶点

半平面交可以求平面中需要满足一些特定条件的区域。

可以用来求多边形的核,放进多边形的圆的最大半径等等

A

[Link](3335 – Rotating Scoreboard (poj.org))

题意

判断多边形内是否存在一点可以看到看到所有的边。

题解

是否有一点可以看到所有的边,即求多边形的内核,我们把所有的点逆时针存储,如果想要看到这个边即可行解的区域一定要在这个边的左边,最后求个半平面交看看是否在对于所有的边都满足的情况下存在这样的区域即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<:<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.14;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Point p[N];
halfplanes hps;
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) {
        cin >> n;
        hps.clear();
        for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
        for (int i = 0; i < n / 2; i ++ ) swap(p[i], p[n - i - 1]);
        for (int i = 0; i < n; i ++ ) hps.push(halfplane(p[i], p[(i + 1) % n]));
        if (!hps.halfplaneinsert()) puts("NO");
        else puts("YES");
    }
    return 0;
}

B

[Link]([kuangbin带你飞]专题二十三 计算几何之半平面交 [Cloned] - Virtual Judge (vjudge.net))

题意

多边形是否存在内核

题解

同上一题一样半平面交判断即可

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<:<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.14;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Point p[N];
halfplanes hps;
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    while (cin >> n, n) {
        hps.clear();
        for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
        for (int i = 0; i < n; i ++ ) hps.push(halfplane(p[i], p[(i + 1) % n]));
        if (hps.halfplaneinsert()) puts("1");
        else puts("0");
    }
    return 0;
}

C

[Link](1474 – Video Surveillance (poj.org))

题意

判断多边形是否存在内核

题解

半平面交直接判断即可

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<:<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.14;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Point p[N];
halfplanes hps;
int main() {
    int T = 1;
    while (cin >> n, n) {
        hps.clear();
        for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
        for (int i = 0; i < n / 2; i ++ ) swap(p[i], p[n - i - 1]);
        for (int i = 0; i < n; i ++ ) hps.push(halfplane(p[i], p[(i + 1) % n]));
        cout << "Floor #" << T ++ << endl;
        if (hps.halfplaneinsert()) puts("Surveillance is possible.");
        else puts("Surveillance is impossible.");
        cout << endl;
    }
    return 0;
}

D

[Link](1279 – Art Gallery (poj.org))

题意

求半平面的面积

题解

求出半平面后,用cross求面积即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 10010, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.14;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Point p[N];
halfplanes hps;
int main() {
   int T; cin >> T;
   while (T --) {
       cin >> n;
       hps.clear();  
       for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
       for (int i = 0; i < n / 2; i ++ ) swap(p[i], p[n - i - 1]);
       for (int i = 0; i < n; i ++ ) hps.push(halfplane(p[i], p[(i + 1) % n]));
       polygon pol;  hps.halfplaneinsert(), hps.getconvex(pol);
       double res = 0;
       for (int i = 1; i < pol.n; i ++ )
        res += fabs((pol.p[i] - pol.p[0]) ^ (pol.p[(i + 1) % pol.n] - pol.p[0])) / 2;
       printf("%.2lf\n", res); 
   }
    return 0;
}

E

[Link](3525 – Most Distant Point from the Sea (poj.org))

题意

给定一个凸多边形,求凸多边形内某点到各边的最小距离的最大值。

题解

预处里出来每条边的法向量然后二分一下向里面平移的距离,对于新的半平面线求一下半平面交看看是否满足条件即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 10010, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.14;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
Vector norm1() {
    Vector c  = e - s;
    double len = sqrt(c.x * c.x + c.y * c.y);
    c.x /= len, c.y /= len;
    double t = c.x;
    c.x = -c.y, c.y = t;
    return c;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Vector Norm(Point a) {
    return Point(-a.y / a.len(), a.x / a.len());
}
Point p[N];
Point normal[N];
halfplanes hps;
int main() {
   while (cin >> n, n) {
       for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
       double l = 0, r = 20000;
       while (r - l > eps) {
           double mid = (l + r) / 2;
           for (int i = 0; i < n; i ++ ) {
                Line t(p[i], p[(i + 1) % n]);
                hps.push(halfplane(p[i] + t.norm1() * mid, p[(i + 1) % n] + t.norm1() * mid));
           }
           if (hps.halfplaneinsert()) l = mid;
           else r = mid;
           hps.clear();
       }
       printf("%.6lf\n", l);
   }
    return 0;
}

F

[Link](3384 – Feng Shui (poj.org))

题意

给定一个多边形,在多边形内放有两个相同的圆,使两个圆尽可能多的覆盖多边形。输出最终两个圆心的位置。

题解

首先将边推进一个R,用半平面交可以随便放的可行区域,贪心来看想要覆盖面积大那么两圆一定越分离越好,因此找到半平面交后的凸多边形上距离最远的两点即可,找最远两点可以暴力枚举也可以旋转卡壳。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 10010, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.14;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
Vector norm1() {
    Vector c  = e - s;
    double len = sqrt(c.x * c.x + c.y * c.y);
    c.x /= len, c.y /= len;
    double t = c.x;
    c.x = -c.y, c.y = t;
    return c;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Vector Norm(Point a) {
    return Point(-a.y / a.len(), a.x / a.len());
}
Point p[N];
Point normal[N];
polygon pol;
halfplanes hps;
int main() {
    double r;
    cin >> n >> r;
    for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
    for (int i = 0; i < n / 2; i ++ ) swap(p[i], p[n - i - 1]);
    for (int i = 0; i < n; i ++ ) {
        Vector v = Norm(p[(i + 1) % n] - p[i]);
     //   cout << (p[i] + v * r).x << (p[i] + v * r).y << ' ' << (p[(i + 1) % n] + v * r).x << (p[(i + 1) % n] + v * r).y << endl;
        hps.push(halfplane(p[i] + v * r, p[(i + 1) % n] + v * r));
    }
    double maxd = -1;
    int res1 = 0, res2 = 1;
    hps.halfplaneinsert();
    hps.getconvex(pol);
  //  cout << pol.n << endl;
    for (int i = 0; i < pol.n; i ++ )
        for (int j = 0; j < pol.n; j ++ )   
            if (sgn(pol.p[i].distance(pol.p[j]) - maxd) > 0) {
                maxd = pol.p[i].distance(pol.p[j]);
                res1 = i, res2 = j;
            }
     printf("%.4lf %.4lf %.4lf %.4lf\n",pol.p[res1].x,pol.p[res1].y,pol.p[res2].x,pol.p[res2].y);
    return 0;
}

G

[Link](1755 – Triathlon (poj.org))

题意

给出n个人的游泳、骑车、跑步的速度,问每一个人在三项任意分配的路程里能否拿第一。

题解

设游泳汽车跑步的路程分别为 x , y , z x,y,z x,y,z,速度分别为 v [ i ] , u [ i ] , w [ i ] v[i],u[i],w[i] v[i],u[i],w[i],那么如果第i个人想获得第一就等价于

x v [ i ] + y u [ i ] + z w [ i ] > x v [ j ] + y u [ j ] + z w [ j ] \frac x{v[i]}+\frac y{u[i]}+ \frac z{w[i]} > \frac x{v[j]}+\frac y{u[j]}+ \frac z{w[j]} v[i]x+u[i]y+w[i]z>v[j]x+u[j]y+w[j]z

x / z v [ i ] + y / z u [ i ] + 1 w [ i ] > x / z v [ j ] + y / z u [ j ] + 1 w [ j ] \frac {x/z}{v[i]}+\frac {y/z}{u[i]}+ \frac 1{w[i]} > \frac {x/z}{v[j]}+\frac {y/z}{u[j]}+ \frac 1{w[j]} v[i]x/z+u[i]y/z+w[i]1>v[j]x/z+u[j]y/z+w[j]1

( 1 v [ i ] − 1 v [ j ] ) × x / z + ( 1 u [ i ] − 1 u [ j ] ) × ( y / z ) + ( 1 w [ i ] − 1 w [ j ] ) > 0 (\frac 1{v[i]} - \frac 1{v[j]})\times x/z+(\frac 1{u[i]}-\frac 1{u[j]})\times(y/z) +(\frac 1{w[i]}-\frac 1{w[j]}) > 0 (v[i]1v[j]1)×x/z+(u[i]1u[j]1)×(y/z)+(w[i]1w[j]1)>0

可以等价看成 A x + B y + C > 0 Ax+By+C>0 Ax+By+C>0

因此题目就转化为对于每一个人,求出和其他人的线,判断这些线是否有解也就是有半平面交即可

在这里插入图片描述

d1是A的正负,d2是B的正负,因此我们取的是大于零,而且存线存的是两点,所以需要自己来搞出来每个线的正确的方向。

最后再加一个框框框住第一象限然后判断即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<"#x"<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 100010, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-10, pi = 3.14, inf = 1e10;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y));
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
Vector norm1() {
    Vector c  = e - s;
    double len = sqrt(c.x * c.x + c.y * c.y);
    c.x /= len, c.y /= len;
    double t = c.x;
    c.x = -c.y, c.y = t;
    return c;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Vector Norm(Point a) {
    return Point(-a.y / a.len(), a.x / a.len());
}
Point p[N];
Point normal[N];
double v[N], u[N], w[N];
polygon pol;
halfplanes hps;
int main() {
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> v[i] >> u[i] >> w[i];
    for (int i = 0; i < n; i ++ ) {
        hps.clear(); bool ok = true; 
        double k = 10000;
        for (int j = 0; j < n;  j ++ ) {
            if (i == j) continue;
            if (v[i] <= v[j] && u[i] <= u[j] && w[i] <= w[j]) {
                ok = false; 
                break;
            }
            if (v[i] > v[j] && u[i] > v[j] && w[i] > w[j]) continue ;
            // double a = (k / v[j] - k / v[i]) + (k / w[i] - k / w[j]),
            //        b = (k / u[j] - k / u[i]) + (k / w[i] - k / w[j]),
            //        c = (k / w[j] - k / w[i]), x1, y1, x2, y2;
            double a = k / v[j] - k / v[i],
                   b = k / u[j] - k / u[i],
                   c = k / w[j] - k / w[i], x1, y1, x2, y2;
            int d1 = sgn(a), d2 = sgn(b), d3 = sgn(c);
            if (!d1) {
                if (!d2) {
                    if (d3 <= 0) {
                        ok = false;
                        break;
                    }
                    continue ; 
                }
                x1 = 0, x2 = d2, y1 = y2 = -c / b; // 上下平移
            }
            else {
                if (!d2) {
                    x1 = x2 = -c / a;
                    y1 = 0, y2 = -d1;
                }
                else {
                    x1 = 0, y1 = -c / b;
                    x2 = d2, y2 = -(c + a * x2) / b;
                }                
            }
            hps.push(halfplane(Point(x1, y1), Point(x2, y2)));
        }
        // cout << hps.n << endl;
        // for (int i = 0; i < hps.n; i ++ ) cout << hps.hp[i].s.x << " " << hps.hp[i].s.y << endl;
        if (ok) {
             hps.push(halfplane(Point(0, 0), Point(inf, 0)));
             hps.push(halfplane(Point(inf, 0), Point(inf, inf)));
             hps.push(halfplane(Point(inf, inf), Point(0, inf)));
             hps.push(halfplane(Point(0, inf), Point(0, 0)));
        }
        //  if (ok) {
        //      hps.push(halfplane(Point(0, 0), Point(inf, 0)));
        //      hps.push(halfplane(Point(0, inf), Point(0, 0)));
        //      hps.push(halfplane(Point(1, 0), Point(0, 1)));
        // }
        if (!ok || !hps.halfplaneinsert()) puts("No");
        else puts("Yes");
        hps.clear();
    }
    return 0;
}

H

[Link](2540 – Hotter Colder (poj.org))

题意

一个“更冷更热”的游戏,就是一个人在猜某个东西的位置,一开始猜的位置一定是(0,0),然后后面每猜一个地方就会被告知“Colder”(离目标更了)“Hotter“(离目标更近了),”Same“(一样近),然后每猜一次,算出可能位置的面积。

题解

连接上一个点pre和当前点now,做这个线段的中垂线,可以发现如果更远那么一定更靠近点pre,否则靠近点now即目标点是在中垂线的左边或右边,如果一定大的话就说明是在中垂线上那么就可以确定为一个点了,因此转化为满足一些直线条件的可行区域,直接用半平面交求出平面再求个面积即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.1415926, inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
double get_area() {
    double res = 0;
    for (int i = 0; i < n; i ++ ) 
        res += ((p[i] - p[0]) ^ (p[(i + 1) % n] - p[0])) / 2;
    return res;
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Vector normn(Vector v) {
    return Vector(-v.y, v.x);
}
Vector norms(Vector v) {
    return Vector(v.y, -v.x);
}
char str[N];
Point pre(0, 0), now;
bool ok = false;
halfplanes hps;
polygon poy;
int main() {
    hps.push(halfplane(Point(0, 10), Point(0, 0)));
    hps.push(halfplane(Point(0, 0), Point(10, 0)));
    hps.push(halfplane(Point(10, 0), Point(10, 10)));
    hps.push(halfplane(Point(10, 10), Point(0, 10)));
    while (cin >> now.x >> now.y >> str) {
        if (!strcmp(str, "Same")) ok = true;
        if (ok) printf("0.00\n");
        else {
            Point o = (pre + now) / 2;
            if (!strcmp(str, "Colder")) {
                Vector p = normn(now - pre);
                hps.push(halfplane(o, o + p));
            }
            else {
                Vector p = norms(now - pre);
                hps.push(halfplane(o, o + p));
            }
            hps.halfplaneinsert(), hps.getconvex(poy);
            printf("%.2lf\n", poy.get_area());
        }
        pre = now; 
    }
    return 0;
}

I

[Link](2451 – Uyuw’s Concert (stdu.edu.cn))

题意

给你出一些直线求半平面交的面积。

题解

直接半平面交求即可,注意加一个框框住可行解。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = -3.1415926, inf = 10000;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
    e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0) return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf %lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
double get_area() {
    double  res = 0;
    for (int i = 1; i < n; i ++ ) 
        res += ((p[i] - p[0]) ^ (p[(i + 1) % n] - p[0])) / 2;
    return res;
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
halfplanes hps;
polygon poy;
int main() {
    cin >> n;
    hps.push(halfplane(Point(0, 0), Point(inf, 0))), hps.push(halfplane(Point(inf, 0), Point(inf, inf)));
    hps.push(halfplane(Point(inf, inf), Point(0, inf))), hps.push(halfplane(Point(0, inf), Point(0, 0)));
    for (int i = 0; i < n; i ++ ) {
        Point a, b; cin >> a.x >> a.y >> b.x >> b.y;
        hps.push(halfplane(a, b));
    }
    hps.halfplaneinsert(), hps.getconvex(poy);
    // double res = 0;
    // for (int i = 1; i < poy.n; i ++ )
    //     res += fabs((poy.p[i] - poy.p[0]) ^ (poy.p[(i + 1) % n] - poy.p[0])) / 2;
    printf("%.1lf\n", poy.get_area());
    return 0;
}

J

[Link](1271 – Nice Milk (stdu.edu.cn))

题意

给你一个n多边形面包,要沾k次高为h的牛奶,问你沾面包的最大面积是多少。

题解

发现每一条边都可以往里推进,因此可以先预处理出来每条线向法线方向推进h以后的线,直接爆搜每一条边是否往里推进,每次求个半平面交,使得半平面交面积取最少即可。

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <bitset>
#include <cmath> 
#include <stack>
#include <iomanip>
#include <deque> 
#include <sstream>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = 3.1415926, inf = 1e20;
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}
int n, m, k;
double h;
double res;
int sgn(double x) {
    if (fabs(x) < eps) return 0;
    if (x < 0)  return -1;
    return 1;
}
struct Point {
double x, y;
Point() {}
Point (double _x, double _y) {
x = _x, y = _y;
}
void input() {
scanf("%lf%lf", &x, &y);
}
void output() {
printf("%.2lf %.2lf", x, y);
}
bool operator ==(Point b) const{
return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
}
bool operator <(Point b) const{
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point &b) const{
return Point(x - b.x, y - b.y);
}
Point operator +(const Point &b) const{
return Point(x + b.x, y + b.y);
}
Point operator *(const double &t) const{
return Point(x * t, y * t);
}
Point operator /(const double &t) const{
return Point(x / t, y / t);
}
//cross
double operator ^(const Point &b) const{
return x * b.y - y * b.x;
}
// dot
double operator *(const Point &b) const{
return x * b.x + y * b.y;
}
double len() {
return sqrt(x * x + y * y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return hypot(x - p.x, y - p.y);
}
// pa pa 夹角 
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)), (a - p) * (b - p)));
}
// 转化为长度为r的向量
Point trunc(double r) {
double l = len();
if (!sgn(l)) return *this;
r /= l;
return Point(x * r, y * r);
}
//逆时针旋转90度
Point rotleft() {
return Point(-y, x);
}
//顺时针旋转90度
Point rotright() {
return Point(y, -x);
}
// 绕着p点逆时针旋转 rad度
Point rotate(Point p, double rad) {
Point v = (*this) - p;
double c = cos(rad), s = sin(rad);
return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
}
};
typedef Point Vector;
struct Line {
   Point s, e;
   Line() {}
   Line(Point _s, Point _e) {
        s = _s, e = _e;
   }
      // 根据一个点和倾斜角 angle 确定直线, 0<=angle< pis
   Line(Point p, double angle) {
      s = p;
     if (sgn(angle - pi / 2) == 0)    e = (s + Point(0, 1));
     else e = (s + Point(1, tan(angle))); // 往上平移
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (!sgn(a)) {
s = Point(0, -c / b);
e = Point(1, -c / b);
}
else if (!sgn(b)) {
s = Point(-c / a, 0);
e = Point(-c / a, 1);
}
else {
s = Point(0, -c / b);
e = Point(1, (-c - a) / b);
}
}
bool operator ==(Line v) {
return (s == v.s) && (e == v.e);
}
void input() {
s.input(), e.input();
}
void adjust() {
if (e < s) swap(s, e);
}
// 线段长度
double length() {
return s.distance(e);
}
// 直线倾斜角 0<=angle<pi
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
// 点和线的关系 1.左边 2.右边 3.线上
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0) return 1;
else if (c > 0) return 2;
return 3;
}
//点在线段上
bool pointonseg(Point p) {
return sgn((p-s)^(e-s)) == 0 && sgn((s-p)*(e-p)) <= 0;
}
// 两直线平行
bool parallel(Line v) {
return sgn((e - s) ^ (v.e - v.s)) == 0;
}
// 两线段相交判断  2 规范相交 1 不规范相交 0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s)), d4 = sgn((v.e-v.s)^(e-v.s));
if ((d1^d2) == -2 && (d3^d4) == -2) return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
 (d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
 (d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
 (d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
// 直线和线段相交判断 2 规范相交 1 不规范相交 0 不相交 this line v seg
int linecorssseg(Line v) {
int d1 = sgn((e-s)^(v.s-s)), d2 = sgn((e-s)^(v.e-s));
if ((d1^d2) == -2) return 2;
return (!d1 || !d2); 
}
// 两直线关系 0 平行 1 重合 2 相交
int linecrossline(Line v) {
if ((*this).parallel(v)) return v.relation(s) == 3;
return 2; 
}
// 求两直线的交点,保证不平行和重合
Point crossPoint(Line v) {
double a1 = (v.e-v.s)^(s-v.s), a2 = (v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
double dispointtoline(Point p) {
return fabs((e-s)^(p-s)) / length();
}
double dispointtoseg(Point p) {
if (sgn((e-s)*(p-s)) < 0 || sgn((e-s)*(p-e)) < 0) return min(p.distance(s), p.distance(e));
return dispointtoline(p);
}
// 返回线段和线段的距离,保证不能相交
double dissegtoseg(Line v) {
return min(min(dispointtoseg(v.s), dispointtoseg(v.e)), 
min(v.dispointtoseg(s), v.dispointtoseg(e)));
}
// 返回点p在直线上的投影
Point lineprog(Point p) {
return s + (((e-s)*((e-s)*(p-s))/(e-s).len2()));
}
};
struct polygon {
int n;
Point p[N];
Line l[N];
void input(int _n) {
n = _n;
for (int i = 0; i < n; i ++ ) p[i].input();
}
void add(Point q) {
p[n ++] = q;
}
void getline() {
for (int i = 0; i < n; i ++ )
l[i] = Line(p[i], p[(i + 1) % n]);
}
struct cmp {
Point p;
cmp(const Point &p0) {
p = p0;
}
bool operator() (const Point &aa, const Point &bb) {
Point a = aa, b = bb;
int d = sgn((a - p) ^ (b - p));
if (!d) return sgn(a.distance(p) - b.distance(p)) < 0;
return d > 0;
}
};
void norm() { // 极角排序
Point mi = p[0];
for (int i = 1; i < n; i ++ ) mi = min(mi, p[i]);
sort(p, p + n, cmp(mi));
}
void getconvex(polygon &convex) { // 上凸包,下凸包、顺时针,rotate
sort(p, p + n);
convex.n = n;
for (int i = 0; i < min(n, 2); i ++ ) {
convex.p[i] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
if (n <= 2) return ;
int &top = convex.n;
top = 1;
for (int i = 2; i < n; i ++ ) {
while (top && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0)  top --;
convex.p[++top] = p[i];
}
int temp = top;
convex.p[++top] = p[n-2];
for (int i = n - 3; i >= 0; i -- ) {
while (top != temp && sgn((convex.p[top] - p[i]) ^ (convex.p[top - 1] - p[i])) <= 0) top --;
convex.p[++top] = p[i];
}
if (convex.n == 2 && (convex.p[0] == convex.p[1])) convex.n --;
convex.norm();
}
double get_area() {
    double res = 0;
    for (int i = 1; i < n; i ++ ) 
        res += ((p[i] - p[0]) ^ (p[(i + 1) % n] - p[0])) / 2;
    return res;
}
};
struct halfplane:public Line { // 一个线和角度
double angle;
halfplane(){}
halfplane(Point _s, Point _e) {
s = _s, e = _e;
}
halfplane(Line v) {
s = v.s, e = v.e;
}
void calcangle() {
angle = atan2(e.y - s.y, e.x - s.x);
}
bool operator <(const halfplane &b) const{
return angle < b.angle;
}
};
struct halfplanes {
int n;
halfplane hp[N];
Point p[N]; // 上一次的交点
int que[N]; // 双端队列
int st, ed;
void clear() {
    n = 0;
}
void push(halfplane tmp) {
hp[n ++] = tmp;
}
void unique() { // 定序的才能开始去重,保证无角度相同的,角度相同的保留更靠里的
int m = 1;
for (int i = 1; i < n; i ++ ) {
if (sgn(hp[i].angle - hp[i - 1].angle) != 0) hp[m ++] = hp[i];
else if (sgn((hp[m-1].e - hp[m-1].s) ^ (hp[i].s - hp[m - 1].s)) > 0) hp[m - 1] = hp[i];
}
n = m;
}
// 得到半平面交
bool halfplaneinsert() { // 看交点是否on_right
for (int i = 0; i < n; i ++ ) hp[i].calcangle();
sort(hp, hp + n);
unique();
que[st=0] = 0, que[ed=1] = 1;
p[1] = hp[0].crossPoint(hp[1]);
for (int i = 2; i < n; i ++ ) { // 存边
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[ed] - hp[i].s)) < 0) ed --;
while (st < ed && sgn((hp[i].e - hp[i].s) ^ (p[st + 1] - hp[i].s)) < 0) st ++;
que[++ ed] = i;
if (hp[i].parallel(hp[que[ed - 1]])) return false;
p[ed] = hp[i].crossPoint(hp[que[ed - 1]]);
}
while (st < ed && sgn((hp[que[st]].e - hp[que[st]].s) ^ (p[ed] - hp[que[st]].s)) < 0) ed --;
while (st < ed && sgn((hp[que[ed]].e - hp[que[ed]].s) ^ (p[st + 1] - hp[que[ed]].s)) <0) st ++;
if (st + 1 >= ed) return false; // 至少三条边才能构成面积
return true;
}
// 通过半平面交得到凸多边形
void getconvex(polygon &con) {
p[st] = hp[que[st]].crossPoint(hp[que[ed]]);
con.n = ed - st + 1;
for (int j = st, i = 0; j <= ed; i ++, j ++ ) 
con.p[i] = p[j];
}
};
Vector get_norm(Vector v) {
    double len = v.len();
    v.x /= len, v.y /= len;
    Vector t;
    t.x = -v.y, t.y = v.x;
    return t;
}
halfplanes hps;
polygon poy;
Line L1[N], L2[N];
Point p[N];
bool st[N];
double check() {
    hps.clear();
    for (int i = 0; i < n; i ++ )
        if (st[i]) hps.push(halfplane(L2[i]));
        else hps.push(halfplane(L1[i]));
    hps.halfplaneinsert(), hps.getconvex(poy);
    return poy.get_area();
}
void dfs(int u, int cnt) {
    if (cnt >= k) {
        res = min(res, check());
        return ;
    }
    if (u >= n) return ;
    dfs(u + 1, cnt);
    st[u] = true;
    dfs(u + 1, cnt + 1);
    st[u] = false;
}
int main() {
    ios::sync_with_stdio(false), cin.tie(0);
    while (cin >> n >> k >> h, n + k + h) {
        res = inf;
        for (int i = 0; i < n; i ++ ) cin >> p[i].x >> p[i].y;
        for (int i = 0; i < n; i ++ ) L1[i].s = p[i], L1[i].e = p[(i + 1) % n];
        for (int i = 0; i < n; i ++ ) {
            Vector t = get_norm(L1[i].e - L1[i].s);
            L2[i].s = (L1[i].s + t * h), L2[i].e = (L1[i].e + t * h);
        }
        k = min(k, n);
        dfs(0, 0);
        hps.clear();
        for (int i = 0; i < n; i ++ ) hps.push(halfplane(L1[i]));
        hps.halfplaneinsert(), hps.getconvex(poy);
        printf("%.2lf\n", poy.get_area() - res);
    }
    return 0;
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值