CS Academy Round #33 Subinterval Division

题意: 二维空间上有集合S,S中有n个点,将x轴上(0,0)到(X,0)分成多个部分,对于每一个部分中的所有点,离S中的最近的点是相同。


注意到任意两个点的连线的中垂线都可以将一个平面分成两个半平面,每个半平面到各自的点是最近的。那么可以维护一个堆栈,对于堆栈中的n个点已经将x轴分成了n+1个部分,那么新加入的点如果不影响栈顶的点,那么也不会影响栈内的其他点,因为现在需要分割的平面是属于栈顶的点的。如果影响栈顶的点,那么pop掉,直至不影响为止。


代码:

#include<bits/stdc++.h>
using namespace std;

const double eps = 1e-10;
const double PI = acos(-1.0);
struct Point { double x, y; Point(double x=0, double y=0):x(x),y(y) {} };
struct idPoint { Point p; int id; };
struct Circle { Point c; double r; Circle(Point c=Point(0, 0), double r = 0):c(c),r(r) {} };
typedef Point Vector;
int dcmp(double x) { if(fabs(x) < eps) return 0; else return x<0?-1:1; }
bool operator < (const Point& a, const Point& b) { return a.x<b.x || (a.x==b.x&&a.y<b.y); }
bool operator < (const idPoint& a, const idPoint& b) { return a.p < b.p; }
bool operator == (const Point& a, const Point& b) { return dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)==0; }
Vector operator + (Vector A, Vector B) { return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (Point A, Point B) { return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); }
Vector operator / (Vector A, double p) { return Vector(A.x/p, A.y/p); }
double Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; }
double Length(Vector A) { return sqrt(Dot(A, A)); }
double Angle(Vector A, Vector B) { return acos(Dot(A, B)/Length(A)/Length(B)); }
double Cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }
Vector Rotate(Vector A, double rad) {
    return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}//向量逆时针旋转
Point GetLineIntersection(Point P, Vector v, Point Q, Vector w) {
    Vector u = P-Q;
    double t = Cross(w, u)/Cross(v, w);
    return P+v*t;
}//求直线交点

const int N = 1e5+5;

double ans[N];
Point lp[N];
idPoint p[N];
stack<int> S;

double solve(int u, int v) {
    Point pp = (p[u].p+p[v].p)/2;
    return GetLineIntersection({0,0}, {1,0}, pp, Rotate(p[v].p-pp, PI/2)).x;
}

int main() {
    int n;
    double X;
    cin >> n >> X;
    for(int i = 0; i < n; i++) {
        scanf("%lf%lf", &p[i].p.x, &p[i].p.y);
        p[i].id = i;
    }
    sort(p, p+n);
    for(int i = 0; i < n; i++) {
        if(S.empty()) {
            S.push(i);
            lp[i].x = 0;
            lp[i].y = X;
            continue;
        }
        while(!S.empty()) {
            int u = S.top();
            double xx = solve(u, i);
            if(xx < lp[u].x) S.pop();
            else {
                lp[u].y = xx;
                lp[i].x = xx;
                lp[i].y = X;
                break;
            }
        }
        S.push(i);
    }
    memset(ans, 0, sizeof(ans));
    while(!S.empty()) {
        int u = S.top();
        S.pop();
        ans[p[u].id] = max(0.0, min(X, lp[u].y)-lp[u].x);
    }
    for(int i = 0; i < n; i++) printf("%.12f\n", ans[i]);
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值