[HNOI2008]水平可见直线

时间:2021-01-31 00:56:14

题目描述

在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的.

例如,对于直线:L1:y=x; L2:y=-x; L3:y=0则L1和L2是可见的,L3是被覆盖的.给出n条直线,表示成y=Ax+B的形式(|A|,|B|<=500000),且n条直线两两不重合.求出所有可见的直线.

输入输出格式

输入格式:

第一行为N(0 < N < 50000),接下来的N行输入Ai,Bi

输出格式:

从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格

输入输出样例

输入样例#1: 复制
3
-1 0
1 0
0 0
输出样例#1: 复制
1 2
实际上就是求一个最长下凸包
没有学过计蒜几何也没关系,可以自己画图理解
按斜率从小到大排序
实际上考虑两种情况:
1.
[HNOI2008]水平可见直线

这种情况显然可以,就是一个下凸包

2.

[HNOI2008]水平可见直线

 

这样显然斜率第2的直线无法看到

这样我们可以发现

如果i>j>k

交点(i,k)在交点(j,k)左边,那么j就不行

于是用单调栈

 1 #include<iostream>
2 #include<cstdio>
3 #include<cstring>
4 #include<algorithm>
5 #include<cmath>
6 using namespace std;
7 struct Node
8 {
9 double a,b;
10 int n;
11 }a[50001];
12 int n,top,s[50001];
13 double p1,p2,eps=1e-8;
14 double cross(int i,int j)
15 {
16 double x=(a[j].b-a[i].b)/(a[i].a-a[j].a);
17 return x;
18 }
19 bool cmp(Node a,Node b)
20 {
21 if (fabs(a.a-b.a)<eps) return a.b<b.b;
22 return a.a<b.a;
23 }
24 int main()
25 {int i,j;
26 cin>>n;
27 for (i=1;i<=n;i++)
28 {
29 scanf("%lf%lf",&a[i].a,&a[i].b);
30 a[i].n=i;
31 }
32 sort(a+1,a+n+1,cmp);
33 for (i=1;i<=n;i++)
34 {
35 while (top)
36 {
37 if (fabs(a[i].a-a[s[top]].a)<eps) top--;
38 else if (top>1&&cross(i,s[top-1])<=cross(s[top-1],s[top])) top--;
39 else break;
40 }
41 s[++top]=i;
42 }
43 for (i=1;i<=top;i++)
44 s[i]=a[s[i]].n;
45 sort(s+1,s+top+1);
46 for (i=1;i<=top;i++)
47 printf("%d ",s[i]);
48 }