冰魄吐息题解

冰魄吐息

题目背景

加强版

题目描述

N 个点,第 i 个点坐标为 (xi,yi)

定义一束激光:y=kx,若点 i 满足到该条直线距离 d,认为被激光击中。

求使用至多 K 束激光,击毁图中所有点的最小代价 d

输入格式

第一行输入两个正整数 N,K

2N+1 行,每行两个非负整数 xi,yi,表示第 i 个点的坐标。

输出格式

第一行输出最小代价 d,保留到小数点后 2 位。

样例 #1

样例输入 #1

2 1
2 3
6 3

样例输出 #1

1.20

样例 #2

样例输入 #2

4 3
1 6
4 6
4 2
4 0

样例输出 #2

0.97

提示

样例说明

样例 #1 中两蓝色点坐标分别为 (2,3)(6,3),到直线 y=34x 的距离均为 65=1.2,因此输出 1.20 样例 #2 中坐标为 (4,0),(4,2) 的点与斜率为 14 的直线之间距离为 417170.97014,因此输出 0.97

可以证明两个样例中的方案是最优的,因此所求的 d 是最小的。

提示

  1. 可能会用到的公式:点 (x0,y0) 到直线 y=kx 的距离是 |y0kx0|k2+1
  2. 对于 C++ 选手,建议使用 变量类型 long double 进行数值计算。
    输出时请用形如 printf("%.2Lf",ans) 的形式输出答案,注意 %.2Lf 中的 L 是大写。
  3. 本题中存在的浮点数运算可能较多,虽然已经增大时限,但仍然请注意常数因子对程序效率的影响。

数据范围

本题采用捆绑测试

M 为所有 xi,yi 的最大值。

Subtask Score M K
1 30 10 1
2 30 1.5×104 1
3 40

对于 100% 的数据:1N,M,K2.5×104

所有数据均保证不存在相同的 (xi,yi) 出现多次。


题解

根据题目,明显的有"最多的最少"这一醒目的词眼,简单分析可得本题具有单调性

那么考虑二分,容易想到,对于一个点能被击中,肯定是以此点为圆心,二分的值为半径的圆与直线有交点

进一步地,稍稍旋转一下,不难发现满足要求的直线的斜率肯定是被原点与此圆相切的两条直线的夹住,这时候分情况讨论

假设我们设圆心为p,半径为rp上方的切线记为l1,下方的切线记为l2,斜率分别为k1,k2,先特殊处理切线为坐标系的情况

明显,为了击中更多的点,我们的直线都过一三象限肯定不会比其他决策差

  1. 0<k1,k2时,斜率在区间[k2,k1]上的都可以与圆p相交
  2. 0<k10>k2的时候,此时l2过二四象限,此时斜率在[0,k1]的直线都与圆相交,此时我们无需考虑[k2,0]的部分,因为射出一条斜率小于0的直线一定不优(因为点都在第一象限)
  3. 0>k1,0<k2的时候,此时l1过二四象限,此时我们同样无需考虑[k1,0]的部分,只需要考虑k2到无穷大的部分即可
  4. 其余情况一三象限任意直线都可以击中

那么我们就可以使用二分法求出有效区间,对于k1的二分直接以直线(O,p)的斜率为下界,无穷大为上界即可,k2则以直线(O,p)的斜率为上界,0为下界二分即可

最终就可以得到每个点的有效斜率区间,此时问题转化为了给定n个区间,需要使用最少的点数使得每个区间至少有一个点。这是一个简单的贪心问题

贪心策略:将所有区间按右端点为第一关键字,左区间为第二关键字排序,则取点一定是在合法情况下尽量往右边取

证明:易知这样取点一定不会让结果变差,根据贪心的决策包容性,命题得证

那么做法就显然了,维护一个变量,表示最后一个点的位置,从1到n扫描,对于每一个区间,判定这个变量是否在区间内,如果不是在区间内,将点的数量加一,并且将变量更新为这个区间的右端点即可

这样我们就得到了最小直线使用数,判断这个数与K比较,便可知道二分的值如何变化了。
至此,本题便得到了解决

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double n, m;
#define N 500050
//#define scanf scanf_s
#define eps 1e-8
#define inf 1e7
#define db double
struct node {
    db l, r;
}a[N], b[N];
bool cmp(node a, node b) {
    return a.r < b.r;
}
db dist(db k, double x, double y) {
    return fabs(y - k * x) / sqrt(k * k + 1);
}
node erfen(double x, double y, db d) {
    node ans = { 0.0,0.0 };
    db l, r;
    if (fabs(x) < eps)return { -y / d,y / d };
    l = y / x, r = inf;//先分上层
    while (l + eps < r) {
        db mid = (l + r) * 0.5;
        if (dist(mid, x, y) <= d) {
            l = mid;
        }
        else r = mid;
    }
    ans.r = l;
    l = 0, r = y / x;
    while (l + eps < r) {
        db mid = (l + r) * 0.5;
        if (dist(mid, x, y) <= d) {
            r = mid;
        }
        else l = mid;
    }
    ans.l = l;
    return ans;
}
double check(db d) {
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i].l * a[i].l + a[i].r * a[i].r <= d) {
            continue;
        }
        else b[++cnt] = erfen(a[i].l, a[i].r, d);
    }
    sort(b + 1, b + cnt + 1, cmp);
    double ans = 0;
    db pos = -inf;
    for (int i = 1; i <= cnt; i++) {
        if (pos <= b[i].l)pos = b[i].r, ans++;
    }
    return ans;
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].l >> a[i].r;
    }
    db l = 0, r = inf, e = 1e-5, ans = 0;
    while (l + e < r) {
        db mid = (l + r) * 0.5;
        if (check(mid) > m)l = mid;
        else r = mid;
    }
    printf("%.2f", r);
}
posted @   spdarkle  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示