P3194 [HNOI2008]水平可见直线
题目描述
在 x−yx-yx−y 直角坐标平面上有 nnn 条直线 L1,L2,…LnL_1,L_2,…L_nL1,L2,…Ln,若在 yyy 值为正无穷大处往下看,能见到 LiL_iLi 的某个子线段,则称 LiL_iLi 为可见的,否则 LiL_iLi 为被覆盖的。 例如,对于直线: L1:y=xL_1:y=xL1:y=x; L2:y=−xL_2:y=-xL2:y=−x; L3:y=0L_3:y=0L3:y=0; 则 L1L_1L1 和 L2L_2L2 是可见的,L3L_3L3 是被覆盖的。给出 nnn 条直线,表示成 y=Ax+By=Ax+By=Ax+B 的形式(∣A∣,∣B∣≤500000|A|,|B| \le 500000∣A∣,∣B∣≤500000),且 nnn 条直线两两不重合,求出所有可见的直线。
输入输出格式
输入格式:第一行为 NNN (0<N<500000<N<500000<N<50000),接下来的 NNN 行输入 Ai,BiA_i,B_iAi,Bi
输出格式:从小到大输出可见直线的编号,两两中间用空格隔开,最后一个数字后面也必须有个空格。
输入输出样例
输入样例#1:
复制
3
-1 0
1 0
0 0
输出样例#1: 复制
1 2
考验脑洞能力的一个题,首先,相同斜率的两条线段,bi较低的一定会被较高的覆盖,然后,把线段按斜率为第一关键字从小到大截距为第二关键字从小到大排序,对于第 i 个线段,如果它与当前没被覆盖的第 i - 2 个线段交点的横坐标比第 i - 1 个线段与 i - 2 个线段交点的横坐标要靠左,那么第 i - 1 个线段一定会被覆盖(上面被第 i 个线段覆盖,下面被第 i - 2 个线段覆盖)。由此可以得出线性的算法。
1 #include<iostream>
2 #include<cstdio>
3 #include<algorithm>
4 #include<string>
5 #include<queue>
6 using namespace std;
7 const int N = 5e4 + 5;
8 int s[N],top,n,ans[N];
9 inline void read(int&x)
10 {
11 x = 0;char c;int sign = 1;
12 do{ c = getchar(); if(c == '-') sign = -1; }while(c < '0' || c>'9');
13 do{ x = x*10 + c -'0';c = getchar(); }while(c <= '9' && c >= '0');
14 x *= sign;
15 }
16 struct str{
17 int a,b,id;
18 }a[N];
19 const bool cmp(str a,str b){ if(a.a != b.a)return a.a >b.a;else return a.b >b.b; }
20 const bool cmop(int a,int b){ return a < b; }
21 double sol(int i,int j){ return (double)(a[i].b - a[j].b)/(double)(a[j].a - a[i].a); }
22 int main()
23 {
24 read(n);
25 for(int i = 1;i <= n;i++)
26 {
27 read(a[i].a);read(a[i].b);
28 a[i].id = i;
29 }
30 sort(a+1,a+1+n,cmp);
31 for(int i = 1;i <= n;i++)
32 {
33 if(a[i].a == a[i-1].a && i != 1) continue;
34 while(top > 1 && sol(s[top],i) >= sol(s[top],s[top - 1])) top--;
35 s[++top] = i;
36 ans[top] = a[i].id;
37 }
38 sort(ans,ans + top + 1,cmop);
39 for(int i = 1;i <= top;i++) printf("%d ",ans[i]);
40 return 0;
41 }