题解:CF1016E Rest In The Shades

题意

平面上有一个点光源 s s s 并以每秒 1 1 1 单位长度的速度从点 ( a , s y ) (a,sy) (a,sy) 移动到点 ( b , s y ) (b,sy) (b,sy),其中 s y < 0 sy<0 sy<0;在 x x x 轴正方向上有 n n n 不相交、不接触的挡板,第 i i i 个档板挡住了 x x x 轴上 [ l i , r i ] [l_i,r_i] [li,ri] 的部分。对于点 ( x , y ) (x,y) (x,y),当它与 s s s 的连线被某个挡板相交或接触时,我们说 ( x , y ) (x,y) (x,y)​ 在阴影中。

现在给定 q q q 个平面上的点,求出这些点在 s s s 移动过程中处于阴影内的总时间。

解法

  • 黑色部分为 x x x 轴,蓝色部分为挡板,红色部分为 s s s 的移动范围。

如图,对于一个点 P P P,连接点 P P P A , B A,B A,B x x x 轴于两点 A ′ , B ′ A',B' A,B(灰色粗线)。我们求出 [ A ′ , B ′ ] [A',B'] [A,B] 中挡板的占比后,就可以通过相似三角形(灰色、青色部分)求出 [ A , B ] [A,B] [A,B] 上能让 P P P 处于阴影中的总距离(深红色线)。我们将给定挡板按照 x x x 坐标排序,两次二分求出 A ′ A' A B ′ B' B 附近的挡板。预处理出挡板长度的前缀和统计这个点的贡献即可。具体见代码。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
const double eps = 1e-9;
struct Nodes { // 同时表示 A,B 两个点和挡板左右边界。
    double x,y;
    Nodes (double u = 0, double v = 0) {
        x = u, y = v;
    }
} X,Y,a[maxn],now;
int n; double sum[maxn];
int Q;
int main() {
    scanf("%lf%lf%lf%d",&X.y,&X.x,&Y.x,&n);
    n ++, Y.y = X.y;
    for (int i = 2;i <= n;i ++)
        scanf("%lf%lf",&a[i].x,&a[i].y),
        sum[i] = sum[i - 1] + (a[i].y - a[i].x);
    a[++ n] = Nodes{1e18,1e18}, sum[n] = sum[n - 1];
    scanf("%d",&Q);
    while (Q --) {
        scanf("%lf%lf",&now.x,&now.y);
        double k = now.y / (now.y - X.y); // 相似三角形
        double L = (X.x - now.x) * k + now.x, R = (Y.x - now.x) * k + now.x;
        if (L <= a[1].x || R >= a[n].y) { // 特判
            printf("%.6f\n",0);
            continue;
        }
        // 两边二分求左右的挡板
        int l = 1, r = n; 
        while (l < r) {
            int mid = (l + r + 1) >> 1;
            if (a[mid].x <= L) l = mid;
            else r = mid - 1;
        }
        double ans = max(0.0,a[l].y - L) - sum[l];
        l = 1, r = n;
        while (l < r) {
            int mid = l + r >> 1;
            if (a[mid].y >= R) r = mid;
            else l = mid + 1;
        }
        ans += sum[r] - min(a[l].y - R, a[l].y - a[l].x);
        printf("%.7lf\n",ans * (Y.x - X.x) / (R - L)); 
    }
    return 0;
}
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值