冰魄吐息题解
冰魄吐息
题目背景
题目描述
定义一束激光:
求使用至多
输入格式
第一行输入两个正整数
第
输出格式
第一行输出最小代价
样例 #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.20 。 |
样例 0.97 。 |
可以证明两个样例中的方案是最优的,因此所求的
提示
- 可能会用到的公式:点
到直线 的距离是 。 - 对于 C++ 选手,建议使用 变量类型
long double
进行数值计算。
输出时请用形如printf("%.2Lf",ans)
的形式输出答案,注意%.2Lf
中的 L 是大写。 - 本题中存在的浮点数运算可能较多,虽然已经增大时限,但仍然请注意常数因子对程序效率的影响。
数据范围
本题采用捆绑测试。
记
Subtask | Score | ||
---|---|---|---|
对于
所有数据均保证不存在相同的
题解
根据题目,明显的有"最多的最少"这一醒目的词眼,简单分析可得本题具有单调性
那么考虑二分,容易想到,对于一个点能被击中,肯定是以此点为圆心,二分的值为半径的圆与直线有交点
进一步地,稍稍旋转一下,不难发现满足要求的直线的斜率肯定是被原点与此圆相切的两条直线的夹住,这时候分情况讨论
假设我们设圆心为
明显,为了击中更多的点,我们的直线都过一三象限肯定不会比其他决策差
- 当
时,斜率在区间 上的都可以与圆 相交 - 当
的时候,此时 过二四象限,此时斜率在 的直线都与圆相交,此时我们无需考虑 的部分,因为射出一条斜率小于0的直线一定不优(因为点都在第一象限) - 当
的时候,此时 过二四象限,此时我们同样无需考虑 的部分,只需要考虑 到无穷大的部分即可 - 其余情况一三象限任意直线都可以击中
那么我们就可以使用二分法求出有效区间,对于
最终就可以得到每个点的有效斜率区间,此时问题转化为了给定
贪心策略:将所有区间按右端点为第一关键字,左区间为第二关键字排序,则取点一定是在合法情况下尽量往右边取
证明:易知这样取点一定不会让结果变差,根据贪心的决策包容性,命题得证
那么做法就显然了,维护一个变量,表示最后一个点的位置,从1到n扫描,对于每一个区间,判定这个变量是否在区间内,如果不是在区间内,将点的数量加一,并且将变量更新为这个区间的右端点即可
这样我们就得到了最小直线使用数,判断这个数与
至此,本题便得到了解决
#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);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!