HNOI 2008 水平可见直线
题目描述 Description
在 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 条直线两两不重合。求出所有可见的直线。
输入描述 Input Description
第一行为 n(0<n<50000),接下来 n行输入 Ai,Bi。
输出描述 Output Description
从小到大输出可见直线的编号,两两中间用空格隔开。
样例输入 Sample Input
3
-1 0
1 0
0 0
样例输出 Sample Output
1 2
数据范围及提示 Data Size & Hint
n<50000
本题题解三部曲:
一部曲:以k为第一关键字,b为第二关键字,进行排序。
二部曲:构建关于直线的单调栈。
三部曲:整理并输出答案。
就这么简单。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 50000 + 5
int n, s[N], t[N];
struct LINE
{
int num;
double k, b;
void init(int i)
{
scanf("%lf%lf", &k, &b);
num = i;
}
bool operator < (const LINE a) const
{
return k < a.k || (k == a.k && b > a.b);
}
}h[N];
void begin()
{
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
h[i].init(i);
sort(h + 1, h + n + 1);
}
double dotx(LINE u, LINE v)
{
return (u.b - v.b) / (v.k - u.k);
}
void work()
{
s[0] = s[1] = 1;
for (int i = 2; i <= n; i ++)
{
if (h[i].k == h[i - 1].k) continue;
while (s[0] > 1 && dotx(h[s[s[0] - 1]], h[i]) <= dotx(h[s[s[0] - 1]], h[s[s[0]]]))
s[0] --;
s[++ s[0]] = i;
}
for (int i = 1; i <= s[0]; i ++)
t[i] = h[s[i]].num;
sort(t + 1, t + s[0] + 1);
}
void end()
{
for (int i = 1; i <= s[0]; i ++)
printf("%d ", t[i]);
}
int main()
{
begin();
work();
end();
return 0;
}