ICPC昆明站2021 I题Mr. Main and Windmills题解

队友开始都在开L题,感觉没有进展我就看了看计算几何,发现意外的简单,后来陆陆续续队友有L题想法就上,WA了几发L的同时我写出了I题,结果发现段错误,原来是题目读错了,稍加修改一发过(英语的重要性)。

据说很多人卡精度,不知道为什么,我把我的定义改成double也还是能过。可能我用的是自己手推的公示,用的是小学二年级就学过的y=kx+b 斜率方程式求。据说别人模板精度改成1e-1就过了。

题面:

题目链接

题意大概是一个火车从一点到另一个点。有n个风车,会随着位置的移动在火车视角看,位置会向左或者向右偏移,求的是h风车相对位置变化的第k次的点。

都说到这个地步了 可以说是非常简单了(自认为,出题人也说比较简单,看看吓跑多少个看到计算几何就跑的。)无非就是求开始的线段方程,然后第hi点和其他所有点的方程与列车行进路线轨迹方程的交点,sort一下,复杂度是nmlogn,当时还有点担心会T因为有点极限,结果直接过了。

代码:

#include "bits/stdc++.h"
#define LD long double//这里使用double也能过,看来不是卡long double的精度

using namespace std;
const int MAXN = 1005;
const double INF = 1e9 + 7;

struct point {
    LD x, y;
}A[MAXN], stk[MAXN];

LD slope (point a, point b) {
    if (b.x == a.x) return INF;
    return (b.y - a.y) / (b.x - a.x);
}

struct line {
    LD k, b;
    point fromP, endP;
    line(){}
    line(point a, point c) {
        k = slope(a, c);
        b = c.y - c.x * k;
        if (k == INF) b = c.x;
        fromP = a, endP = c;
    }
    void out () {
        printf("k:%LF, b:%LF\n", k, b);
    }
};

point findPoint(line a, line b) {//找到两个线的交点
    if (a.k == b.k) return point{INF, INF};
    LD xpos, ypos;
    if (a.k == INF) {
        xpos = a.fromP.x;
    }else if (b.k == INF) {
        xpos = b.fromP.x;
    }else {
        xpos = (b.b - a.b) / (a.k - b.k);
    }
    if (a.k == 0) {
        ypos = a.fromP.y;
    }else if (b.k == 0) {
        ypos = b.fromP.y;
    }else {
        ypos = xpos * a.k + a.b;
    }
    return point{xpos, ypos};
}

int getAllPoint(int n, int p, line city) {//求得该点和所有点连线与轨道线的交点
    int cnt = 0;
    point s = city.fromP, t = city.endP;
    for (int i = 1; i <= n; ++i) {
        if (i == p)continue;
        line now = line(A[i], A[p]);
        if (city.k == now.k) {//平行必定没交点
            continue;
        }
        point get = findPoint(city, now);
        if (get.x < min(s.x, t.x) || get.x > max(s.x, t.x) || get.y > max(s.y, t.y) || get.y < min(s.y, t.y)) {
            continue;
        }
        stk[++cnt] = get;
    }
    return cnt;
}

int main(void) {
    int n, m;
    cin >> n >> m;
    point s, t;
    cin >> s.x >> s.y >> t.x >> t.y;
    line city = line(s, t);
    //city.out();
    for (int i = 1; i <= n; ++i) {
        cin >> A[i].x >> A[i].y;
    }
    for (int i = 0; i < m; ++i) {
        int h, k;
        cin >> h >> k;
        int len = getAllPoint(n, h, city);
        if (city.k == 0) {//将所有点排序,如果k是0,即平行于x轴就以x坐标排序,不然用y坐标排序
            sort(stk + 1, stk + 1 + len,[](point a, point b){return a.x < b.x;});
        }else {
            sort(stk + 1, stk + 1 + len, [](point a,point b){return a.y < b.y;});
        }
        if (len < k) {
            cout << -1 << endl;
        }else {
            if (s.y > t.y) reverse(stk + 1, stk + 1 + len);
            if (city.k == 0 && s.x > t.x) reverse(stk + 1, stk + 1 + len);
            printf("%.10LF %.10LF\n", stk[k].x, stk[k].y);
        }
    }
    return 0;
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值