Luogu P3194 [HNOI2008] 水平可见直线 题解

Description

在平面直角坐标系中给定若干条直线,问在 \(y\) 值为正无穷处往下看有多少条直线可见。当能看见一条直线的某个子线段时我们称一条直线可见,否则称这条直线被覆盖。

Solution

从最简单的情况开始考虑。若只有两条直线,那么其中一条直线不可见当且仅当两条直线的斜率相同并且截距不同。此时截距较小的那条直线不可见。在以后的讨论中,我们只需要讨论斜率不同的情况就可以了。

考虑三条直线的情况。设三条直线为 \(l_1,l_2,l_3\)。我们用 \(k\) 来表示斜率,不妨设三条直线的斜率 \(k_1<k_2<k_3\)。显然斜率最小的直线 \(l_1\) 和斜率最大的直线 \(l_3\) 都不可能被覆盖,只有 \(l_2\) 可能被 \(l_1\)\(l_3\) 覆盖。

那么什么时候 \(l_2\) 会被覆盖呢?我们画个图看看。

在情况 1 当中,\(l_2\)\(l_1\)\(l_3\) 覆盖,此时 \(l_2\)\(l_1\)\(l_3\) 的交点 \(P\) 的下方。在情况 1 中,\(l_2\) 没有 和 \(l_3\) 覆盖,\(l_2\)\(P\) 的上方。于是,我们只需要判断 \(l_2\)\(P\) 的位置关系就可以了。

判断点和直线的位置关系?这一听就是向量干的事。将在 \(l_2\) 上随便取两个点 \(A,B\),将 \(l_2\) 看作向量 \(\overrightarrow{AB}\) ,用 \(\overrightarrow{AP}\)\(\overrightarrow{AB}\) 的叉积判断一下即可。

在实现的时候,我们可以先将所有直线以斜率为第一关键字,以截距为第二关键字排序。之后我们依次扫描每一条直线,并将直线存入一个栈中。每一次插入直线之前用 while 循环判断一下栈顶的直线是否被当前直线和栈顶下面的那条直线覆盖,如果被覆盖则将其弹出。

考虑为什么这样做是正确的。设从栈顶往下的三条直线依次是 \(l_3,l_2,l_1\),当前准备插入的直线是 \(l_4\)。画一画图很容易发现,若 \(l_4\)\(l_1\) 能覆盖 \(l_2\),那么 \(l_4\)\(l_2\) 一定能够覆盖 \(l_3\),否则 \(l_2\) 就不应该出现在栈中,因为它已经被 \(l_1\)\(l_3\) 覆盖了。也就是说,不会出现在栈中的位置靠下的直线原本应当被弹出,但因为在栈中的位置靠上的直线没有被弹出而没有被弹出的情况。正确性得证。

Code

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10;
const int INF=0x3f3f3f3f;
struct Line{
	int k,b,id;
}a[N],sta[N];
struct Point{
	double x,y;
};
int top;
bool cmp(Line l1,Line l2) {return l1.k==l2.k?l1.b>l2.b:l1.k<l2.k;}
Point calc(Line l1,Line l2) {return {1.0*(l2.b-l1.b)/(l1.k-l2.k),1.0*l1.k*(l2.b-l1.b)/(l1.k-l2.k)+l1.b};}
Point get_point(int x,Line l) {return {1.0*x,1.0*((l.k)*x+(l.b))};}
double cross(Point a,Point b,Point c) {return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&a[i].k,&a[i].b);
		a[i].id=i;
	}
	sort(a+1,a+n+1,cmp);
	a[0].k=INF;
	for(int i=1;i<=n;i++)
	{
		if(a[i].k==a[i-1].k) continue;
		while(top>1&&cross(get_point(0,sta[top]),get_point(1,sta[top]),calc(a[i],sta[top-1]))>=0) top--;
		sta[++top]=a[i];
	}
	sort(sta+1,sta+top+1,[](Line l1,Line l2){return l1.id<l2.id;});
	for(int i=1;i<=top;i++) printf("%d ",sta[i].id);
	return 0;
}
posted @   __Star_Sky  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
点击右上角即可分享
微信分享提示